Vlada645 13 часов назад
Родитель
Сommit
ce6c697666
100 измененных файлов с 14860 добавлено и 0 удалено
  1. BIN
      final project/database/uploads/25ee4153-6839-48d2-8857-9799941e66c7_liu-dakai-20211101.jpg
  2. BIN
      final project/database/uploads/44d680e8-b62c-433b-8585-de053812ce8b_2024-08-28_18-48.png
  3. BIN
      final project/database/uploads/45a31726-4db9-4758-9068-683dd6a07053_2024-08-28_17-16_1.png
  4. BIN
      final project/database/uploads/4c7e2cfa-237d-4d09-8670-0ebd4e0d0e2c_0_cut_min_1-768x768.jpg
  5. BIN
      final project/database/uploads/4ef4cd02-f8dd-4b2b-bfd8-33893fa0ba1d_2024-08-28_18-18.png
  6. BIN
      final project/database/uploads/58f64f96-999e-4d1c-8558-da77b7e2daef_2024-08-30_18-34.png
  7. BIN
      final project/database/uploads/5deac871-7036-41ed-8ca1-81b20718f350_2024-08-28_17-15_1.png
  8. BIN
      final project/database/uploads/7f0dc240-69fc-4ef2-8a6b-cc8b4648175c_2024-08-28_18-18.png
  9. BIN
      final project/database/uploads/86f8517b-8736-4567-9b09-c8469b43410e_2024-08-28_22-20.png
  10. BIN
      final project/database/uploads/a7de2ca6-6cce-485a-a270-f226994286ee_2024-08-28_17-15_1.png
  11. BIN
      final project/database/uploads/be264e3e-c761-4e33-a65a-1b64ab9a6af7_2024-08-28_17-28.png
  12. BIN
      final project/database/uploads/d5ee685f-ae0e-4c34-a1cc-9724dda4577f_2024-08-28_18-48.png
  13. BIN
      final project/database/uploads/dbc6bdbe-8f3a-47b6-8127-1e87a19ae936_2024-08-28_17-28.png
  14. BIN
      final project/database/uploads/e06deff6-1e51-48df-9fa3-76195cc9f8a2_d895bbf8ddf83a6c42c7cce32d3143f9.jpg
  15. BIN
      final project/database/uploads/e7e737a5-09b8-4e7d-948e-71a8a381356d_01df106e-c882-431a-88e0-6edf2fffefa1.jpg
  16. BIN
      final project/database/uploads/ed86ea22-a4fa-41c7-a32c-e95e400245f6_2024-08-28_22-16.png
  17. BIN
      final project/database/uploads/fb3db538-5d02-4266-91d4-2c35b59a2a47_8084044.jpg
  18. 17 0
      final project/web-game-shop/.editorconfig
  19. 44 0
      final project/web-game-shop/.gitignore
  20. 12 0
      final project/web-game-shop/.prettierrc
  21. 59 0
      final project/web-game-shop/README.md
  22. 73 0
      final project/web-game-shop/angular.json
  23. 8033 0
      final project/web-game-shop/package-lock.json
  24. 33 0
      final project/web-game-shop/package.json
  25. BIN
      final project/web-game-shop/public/favicon.ico
  26. 13 0
      final project/web-game-shop/src/app/app.config.ts
  27. 0 0
      final project/web-game-shop/src/app/app.css
  28. 8 0
      final project/web-game-shop/src/app/app.html
  29. 34 0
      final project/web-game-shop/src/app/app.routes.ts
  30. 23 0
      final project/web-game-shop/src/app/app.spec.ts
  31. 15 0
      final project/web-game-shop/src/app/app.ts
  32. BIN
      final project/web-game-shop/src/app/assets/avatar.jpg
  33. BIN
      final project/web-game-shop/src/app/assets/background-register-login.png
  34. BIN
      final project/web-game-shop/src/app/assets/cyberpunk-bg.png
  35. BIN
      final project/web-game-shop/src/app/assets/dark-angel.png
  36. 130 0
      final project/web-game-shop/src/app/components/bug-report/bug-report.css
  37. 94 0
      final project/web-game-shop/src/app/components/bug-report/bug-report.html
  38. 22 0
      final project/web-game-shop/src/app/components/bug-report/bug-report.spec.ts
  39. 95 0
      final project/web-game-shop/src/app/components/bug-report/bug-report.ts
  40. 215 0
      final project/web-game-shop/src/app/components/cart/cart.css
  41. 44 0
      final project/web-game-shop/src/app/components/cart/cart.html
  42. 22 0
      final project/web-game-shop/src/app/components/cart/cart.spec.ts
  43. 149 0
      final project/web-game-shop/src/app/components/cart/cart.ts
  44. 0 0
      final project/web-game-shop/src/app/components/developer/developer.css
  45. 54 0
      final project/web-game-shop/src/app/components/developer/developer.html
  46. 23 0
      final project/web-game-shop/src/app/components/developer/developer.spec.ts
  47. 86 0
      final project/web-game-shop/src/app/components/developer/developer.ts
  48. 130 0
      final project/web-game-shop/src/app/components/footer/footer.css
  49. 54 0
      final project/web-game-shop/src/app/components/footer/footer.html
  50. 22 0
      final project/web-game-shop/src/app/components/footer/footer.spec.ts
  51. 9 0
      final project/web-game-shop/src/app/components/footer/footer.ts
  52. 90 0
      final project/web-game-shop/src/app/components/game-catalog/game-catalog.css
  53. 36 0
      final project/web-game-shop/src/app/components/game-catalog/game-catalog.html
  54. 23 0
      final project/web-game-shop/src/app/components/game-catalog/game-catalog.spec.ts
  55. 173 0
      final project/web-game-shop/src/app/components/game-catalog/game-catalog.ts
  56. 332 0
      final project/web-game-shop/src/app/components/game-details/game-details.css
  57. 113 0
      final project/web-game-shop/src/app/components/game-details/game-details.html
  58. 22 0
      final project/web-game-shop/src/app/components/game-details/game-details.spec.ts
  59. 132 0
      final project/web-game-shop/src/app/components/game-details/game-details.ts
  60. 423 0
      final project/web-game-shop/src/app/components/header/header.css
  61. 94 0
      final project/web-game-shop/src/app/components/header/header.html
  62. 21 0
      final project/web-game-shop/src/app/components/header/header.spec.ts
  63. 117 0
      final project/web-game-shop/src/app/components/header/header.ts
  64. 142 0
      final project/web-game-shop/src/app/components/library/library.css
  65. 42 0
      final project/web-game-shop/src/app/components/library/library.html
  66. 22 0
      final project/web-game-shop/src/app/components/library/library.spec.ts
  67. 87 0
      final project/web-game-shop/src/app/components/library/library.ts
  68. 324 0
      final project/web-game-shop/src/app/components/login/login.css
  69. 92 0
      final project/web-game-shop/src/app/components/login/login.html
  70. 22 0
      final project/web-game-shop/src/app/components/login/login.spec.ts
  71. 96 0
      final project/web-game-shop/src/app/components/login/login.ts
  72. 411 0
      final project/web-game-shop/src/app/components/profile/profile.css
  73. 316 0
      final project/web-game-shop/src/app/components/profile/profile.html
  74. 78 0
      final project/web-game-shop/src/app/components/profile/profile.spec.ts
  75. 361 0
      final project/web-game-shop/src/app/components/profile/profile.ts
  76. 338 0
      final project/web-game-shop/src/app/components/register/register.css
  77. 121 0
      final project/web-game-shop/src/app/components/register/register.html
  78. 22 0
      final project/web-game-shop/src/app/components/register/register.spec.ts
  79. 75 0
      final project/web-game-shop/src/app/components/register/register.ts
  80. 482 0
      final project/web-game-shop/src/app/components/storefront/storefront.css
  81. 132 0
      final project/web-game-shop/src/app/components/storefront/storefront.html
  82. 22 0
      final project/web-game-shop/src/app/components/storefront/storefront.spec.ts
  83. 114 0
      final project/web-game-shop/src/app/components/storefront/storefront.ts
  84. 17 0
      final project/web-game-shop/src/app/interfaces/auth.ts
  85. 13 0
      final project/web-game-shop/src/app/models/report.ts
  86. 22 0
      final project/web-game-shop/src/app/models/routing/routing-module.ts
  87. 16 0
      final project/web-game-shop/src/app/services/api.spec.ts
  88. 38 0
      final project/web-game-shop/src/app/services/api.ts
  89. 16 0
      final project/web-game-shop/src/app/services/bug-report/bug-report.spec.ts
  90. 16 0
      final project/web-game-shop/src/app/services/bug-report/bug-report.ts
  91. 16 0
      final project/web-game-shop/src/app/services/cart/cart.spec.ts
  92. 88 0
      final project/web-game-shop/src/app/services/cart/cart.ts
  93. 16 0
      final project/web-game-shop/src/app/services/developer/developer-service.spec.ts
  94. 6 0
      final project/web-game-shop/src/app/services/developer/developer-service.ts
  95. 16 0
      final project/web-game-shop/src/app/services/game/game.spec.ts
  96. 47 0
      final project/web-game-shop/src/app/services/game/game.ts
  97. 16 0
      final project/web-game-shop/src/app/services/library/library.spec.ts
  98. 39 0
      final project/web-game-shop/src/app/services/library/library.ts
  99. 16 0
      final project/web-game-shop/src/app/services/user/user.spec.ts
  100. 74 0
      final project/web-game-shop/src/app/services/user/user.ts

BIN
final project/database/uploads/25ee4153-6839-48d2-8857-9799941e66c7_liu-dakai-20211101.jpg


BIN
final project/database/uploads/44d680e8-b62c-433b-8585-de053812ce8b_2024-08-28_18-48.png


BIN
final project/database/uploads/45a31726-4db9-4758-9068-683dd6a07053_2024-08-28_17-16_1.png


BIN
final project/database/uploads/4c7e2cfa-237d-4d09-8670-0ebd4e0d0e2c_0_cut_min_1-768x768.jpg


BIN
final project/database/uploads/4ef4cd02-f8dd-4b2b-bfd8-33893fa0ba1d_2024-08-28_18-18.png


BIN
final project/database/uploads/58f64f96-999e-4d1c-8558-da77b7e2daef_2024-08-30_18-34.png


BIN
final project/database/uploads/5deac871-7036-41ed-8ca1-81b20718f350_2024-08-28_17-15_1.png


BIN
final project/database/uploads/7f0dc240-69fc-4ef2-8a6b-cc8b4648175c_2024-08-28_18-18.png


BIN
final project/database/uploads/86f8517b-8736-4567-9b09-c8469b43410e_2024-08-28_22-20.png


BIN
final project/database/uploads/a7de2ca6-6cce-485a-a270-f226994286ee_2024-08-28_17-15_1.png


BIN
final project/database/uploads/be264e3e-c761-4e33-a65a-1b64ab9a6af7_2024-08-28_17-28.png


BIN
final project/database/uploads/d5ee685f-ae0e-4c34-a1cc-9724dda4577f_2024-08-28_18-48.png


BIN
final project/database/uploads/dbc6bdbe-8f3a-47b6-8127-1e87a19ae936_2024-08-28_17-28.png


BIN
final project/database/uploads/e06deff6-1e51-48df-9fa3-76195cc9f8a2_d895bbf8ddf83a6c42c7cce32d3143f9.jpg


BIN
final project/database/uploads/e7e737a5-09b8-4e7d-948e-71a8a381356d_01df106e-c882-431a-88e0-6edf2fffefa1.jpg


BIN
final project/database/uploads/ed86ea22-a4fa-41c7-a32c-e95e400245f6_2024-08-28_22-16.png


BIN
final project/database/uploads/fb3db538-5d02-4266-91d4-2c35b59a2a47_8084044.jpg


+ 17 - 0
final project/web-game-shop/.editorconfig

@@ -0,0 +1,17 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+ij_typescript_use_double_quotes = false
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false

+ 44 - 0
final project/web-game-shop/.gitignore

@@ -0,0 +1,44 @@
+# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
+
+# Compiled output
+/dist
+/tmp
+/out-tsc
+/bazel-out
+
+# Node
+/node_modules
+npm-debug.log
+yarn-error.log
+
+# IDEs and editors
+.idea/
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# Visual Studio Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/mcp.json
+.history/*
+
+# Miscellaneous
+/.angular/cache
+.sass-cache/
+/connect.lock
+/coverage
+/libpeerconnection.log
+testem.log
+/typings
+__screenshots__/
+
+# System files
+.DS_Store
+Thumbs.db

+ 12 - 0
final project/web-game-shop/.prettierrc

@@ -0,0 +1,12 @@
+{
+  "printWidth": 100,
+  "singleQuote": true,
+  "overrides": [
+    {
+      "files": "*.html",
+      "options": {
+        "parser": "angular"
+      }
+    }
+  ]
+}

+ 59 - 0
final project/web-game-shop/README.md

@@ -0,0 +1,59 @@
+# WebGameShop
+
+This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.11.
+
+## Development server
+
+To start a local development server, run:
+
+```bash
+ng serve
+```
+
+Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
+
+## Code scaffolding
+
+Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
+
+```bash
+ng generate component component-name
+```
+
+For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
+
+```bash
+ng generate --help
+```
+
+## Building
+
+To build the project run:
+
+```bash
+ng build
+```
+
+This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
+
+## Running unit tests
+
+To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
+
+```bash
+ng test
+```
+
+## Running end-to-end tests
+
+For end-to-end (e2e) testing, run:
+
+```bash
+ng e2e
+```
+
+Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
+
+## Additional Resources
+
+For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

+ 73 - 0
final project/web-game-shop/angular.json

@@ -0,0 +1,73 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "version": 1,
+  "cli": {
+    "packageManager": "npm"
+  },
+  "newProjectRoot": "projects",
+  "projects": {
+    "web-game-shop": {
+      "projectType": "application",
+      "schematics": {},
+      "root": "",
+      "sourceRoot": "src",
+      "prefix": "app",
+      "architect": {
+        "build": {
+          "builder": "@angular/build:application",
+          "options": {
+            "browser": "src/main.ts",
+            "tsConfig": "tsconfig.app.json",
+            "assets": [
+              {
+                "glob": "**/*",
+                "input": "public"
+              }
+            ],
+            "styles": [
+              "src/styles.css"
+            ]
+          },
+          "configurations": {
+            "production": {
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "500kB",
+                  "maximumError": "1MB"
+                },
+                {
+                  "type": "anyComponentStyle",
+                  "maximumWarning": "4kB",
+                  "maximumError": "8kB"
+                }
+              ],
+              "outputHashing": "all"
+            },
+            "development": {
+              "optimization": false,
+              "extractLicenses": false,
+              "sourceMap": true
+            }
+          },
+          "defaultConfiguration": "production"
+        },
+        "serve": {
+          "builder": "@angular/build:dev-server",
+          "configurations": {
+            "production": {
+              "buildTarget": "web-game-shop:build:production"
+            },
+            "development": {
+              "buildTarget": "web-game-shop:build:development"
+            }
+          },
+          "defaultConfiguration": "development"
+        },
+        "test": {
+          "builder": "@angular/build:unit-test"
+        }
+      }
+    }
+  }
+}

+ 8033 - 0
final project/web-game-shop/package-lock.json

@@ -0,0 +1,8033 @@
+{
+  "name": "web-game-shop",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "web-game-shop",
+      "version": "0.0.0",
+      "dependencies": {
+        "@angular/common": "^21.2.0",
+        "@angular/compiler": "^21.2.0",
+        "@angular/core": "^21.2.0",
+        "@angular/forms": "^21.2.0",
+        "@angular/platform-browser": "^21.2.0",
+        "@angular/router": "^21.2.0",
+        "@stripe/stripe-js": "^9.8.0",
+        "rxjs": "~7.8.0",
+        "tslib": "^2.3.0"
+      },
+      "devDependencies": {
+        "@angular/build": "^21.2.11",
+        "@angular/cli": "^21.2.11",
+        "@angular/compiler-cli": "^21.2.0",
+        "jsdom": "^28.0.0",
+        "prettier": "^3.8.1",
+        "typescript": "~5.9.2",
+        "vitest": "^4.0.8"
+      }
+    },
+    "node_modules/@acemir/cssom": {
+      "version": "0.9.31",
+      "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz",
+      "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==",
+      "dev": true
+    },
+    "node_modules/@algolia/abtesting": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.14.1.tgz",
+      "integrity": "sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-abtesting": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.48.1.tgz",
+      "integrity": "sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-analytics": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.48.1.tgz",
+      "integrity": "sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-common": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.48.1.tgz",
+      "integrity": "sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-insights": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.48.1.tgz",
+      "integrity": "sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-personalization": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.48.1.tgz",
+      "integrity": "sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-query-suggestions": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.48.1.tgz",
+      "integrity": "sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/client-search": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.48.1.tgz",
+      "integrity": "sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/ingestion": {
+      "version": "1.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.48.1.tgz",
+      "integrity": "sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/monitoring": {
+      "version": "1.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.48.1.tgz",
+      "integrity": "sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/recommend": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.48.1.tgz",
+      "integrity": "sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/requester-browser-xhr": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.48.1.tgz",
+      "integrity": "sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/requester-fetch": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.48.1.tgz",
+      "integrity": "sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@algolia/requester-node-http": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.48.1.tgz",
+      "integrity": "sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/client-common": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "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/@angular-devkit/architect": {
+      "version": "0.2102.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2102.11.tgz",
+      "integrity": "sha512-t7J8aaUho1mXjiIecPNX5/rjXeV8j8ZCGY5tD3ic5kzKxPkbuYYcQpJLdzlmBcN+wDgCmNdo8ySvItvU0m58lg==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "21.2.11",
+        "rxjs": "7.8.2"
+      },
+      "bin": {
+        "architect": "bin/cli.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/core": {
+      "version": "21.2.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.2.11.tgz",
+      "integrity": "sha512-kfMNh5X2hOdyr0uNFaaHUJR3OVr4oH2+UhI+FsTu7gqogdgYlHAVHhHAFulfDgtAEOiqpeSQF9RhQnCJl+/LXA==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "8.18.0",
+        "ajv-formats": "3.0.1",
+        "jsonc-parser": "3.3.1",
+        "picomatch": "4.0.4",
+        "rxjs": "7.8.2",
+        "source-map": "0.7.6"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "chokidar": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "chokidar": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics": {
+      "version": "21.2.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.2.11.tgz",
+      "integrity": "sha512-69CWZ5/ftLdpUPAwwdAxTNosiGXUyvwdnOfmHsd9NvCT0OSTeq0eQ0UfnGcHASrXIVmnyWiNfBWM1DLqsgBXmw==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "21.2.11",
+        "jsonc-parser": "3.3.1",
+        "magic-string": "0.30.21",
+        "ora": "9.3.0",
+        "rxjs": "7.8.2"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular/build": {
+      "version": "21.2.11",
+      "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.11.tgz",
+      "integrity": "sha512-2afR6VKkP0HH2u6OuijSMgSHsL5tU4CBCixgQtY677mlvS8TOZg/kOksJIUlz0EvDVCJZBK8WLH9cPJ6mC/Qdg==",
+      "dev": true,
+      "dependencies": {
+        "@ampproject/remapping": "2.3.0",
+        "@angular-devkit/architect": "0.2102.11",
+        "@babel/core": "7.29.0",
+        "@babel/helper-annotate-as-pure": "7.27.3",
+        "@babel/helper-split-export-declaration": "7.24.7",
+        "@inquirer/confirm": "5.1.21",
+        "@vitejs/plugin-basic-ssl": "2.1.4",
+        "beasties": "0.4.1",
+        "browserslist": "^4.26.0",
+        "esbuild": "0.27.3",
+        "https-proxy-agent": "7.0.6",
+        "istanbul-lib-instrument": "6.0.3",
+        "jsonc-parser": "3.3.1",
+        "listr2": "9.0.5",
+        "magic-string": "0.30.21",
+        "mrmime": "2.0.1",
+        "parse5-html-rewriting-stream": "8.0.0",
+        "picomatch": "4.0.4",
+        "piscina": "5.1.4",
+        "rolldown": "1.0.0-rc.4",
+        "sass": "1.97.3",
+        "semver": "7.7.4",
+        "source-map-support": "0.5.21",
+        "tinyglobby": "0.2.15",
+        "undici": "7.24.4",
+        "vite": "7.3.2",
+        "watchpack": "2.5.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "optionalDependencies": {
+        "lmdb": "3.5.1"
+      },
+      "peerDependencies": {
+        "@angular/compiler": "^21.0.0",
+        "@angular/compiler-cli": "^21.0.0",
+        "@angular/core": "^21.0.0",
+        "@angular/localize": "^21.0.0",
+        "@angular/platform-browser": "^21.0.0",
+        "@angular/platform-server": "^21.0.0",
+        "@angular/service-worker": "^21.0.0",
+        "@angular/ssr": "^21.2.11",
+        "karma": "^6.4.0",
+        "less": "^4.2.0",
+        "ng-packagr": "^21.0.0",
+        "postcss": "^8.4.0",
+        "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
+        "tslib": "^2.3.0",
+        "typescript": ">=5.9 <6.0",
+        "vitest": "^4.0.8"
+      },
+      "peerDependenciesMeta": {
+        "@angular/core": {
+          "optional": true
+        },
+        "@angular/localize": {
+          "optional": true
+        },
+        "@angular/platform-browser": {
+          "optional": true
+        },
+        "@angular/platform-server": {
+          "optional": true
+        },
+        "@angular/service-worker": {
+          "optional": true
+        },
+        "@angular/ssr": {
+          "optional": true
+        },
+        "karma": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "ng-packagr": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "tailwindcss": {
+          "optional": true
+        },
+        "vitest": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/cli": {
+      "version": "21.2.11",
+      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.2.11.tgz",
+      "integrity": "sha512-vpF/oa+HzLl4lF78ePCgkhBdQj29IlFvZtBsbAXXpb16FLZSua2m7+yHd/PICTlchh1+LfIxFY9snMY1BllBsQ==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/architect": "0.2102.11",
+        "@angular-devkit/core": "21.2.11",
+        "@angular-devkit/schematics": "21.2.11",
+        "@inquirer/prompts": "7.10.1",
+        "@listr2/prompt-adapter-inquirer": "3.0.5",
+        "@modelcontextprotocol/sdk": "1.26.0",
+        "@schematics/angular": "21.2.11",
+        "@yarnpkg/lockfile": "1.1.0",
+        "algoliasearch": "5.48.1",
+        "ini": "6.0.0",
+        "jsonc-parser": "3.3.1",
+        "listr2": "9.0.5",
+        "npm-package-arg": "13.0.2",
+        "pacote": "21.3.1",
+        "parse5-html-rewriting-stream": "8.0.0",
+        "semver": "7.7.4",
+        "yargs": "18.0.0",
+        "zod": "4.3.6"
+      },
+      "bin": {
+        "ng": "bin/ng.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular/common": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.13.tgz",
+      "integrity": "sha512-fNvRmGAX0zbsLX/kJjgb6l8HAuGTpfYRNc06taTCIvED2RsRpfwrh79IxYlPBspr+hpFbHa0/kxU6Q5I8V0jKQ==",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "21.2.13",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
+    "node_modules/@angular/compiler": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.13.tgz",
+      "integrity": "sha512-0OZk5ujHgowRme3iXJ1Ce1OI3eTDcGovBARBiyJT0E8kt9Y0TdQdGaYMRrNN1UzDv4hk8f1d/xVeF0BpMTvqPQ==",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@angular/compiler-cli": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.13.tgz",
+      "integrity": "sha512-ueETJy2ZcXZ4a0aLEr+oPMw26f8Hn903WC4QN0MCH+sLB9Zustpzydqtmzo5mdSzwuoLoxcesYJTZFmpwD1xIQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "7.29.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14",
+        "chokidar": "^5.0.0",
+        "convert-source-map": "^1.5.1",
+        "reflect-metadata": "^0.2.0",
+        "semver": "^7.0.0",
+        "tslib": "^2.3.0",
+        "yargs": "^18.0.0"
+      },
+      "bin": {
+        "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
+        "ngc": "bundles/src/bin/ngc.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/compiler": "21.2.13",
+        "typescript": ">=5.9 <6.1"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/core": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.13.tgz",
+      "integrity": "sha512-23tS4oNL8nvkHcI4l9rbruQs2WS4yqQmBVQxWakqS9cmRpArLGgveR+hKNU5tPXm5EAi8oLO34/Zy7z70jUpCg==",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/compiler": "21.2.13",
+        "rxjs": "^6.5.3 || ^7.4.0",
+        "zone.js": "~0.15.0 || ~0.16.0"
+      },
+      "peerDependenciesMeta": {
+        "@angular/compiler": {
+          "optional": true
+        },
+        "zone.js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/forms": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.2.13.tgz",
+      "integrity": "sha512-efAKdL8eVRlGvcJWrUFcYyRE/togWfopUTw2D5TIkDAndnmmRaWA70wD4n/E1FFV5UdxSBxoyEYE0qVlPiewtQ==",
+      "dependencies": {
+        "@standard-schema/spec": "^1.0.0",
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "21.2.13",
+        "@angular/core": "21.2.13",
+        "@angular/platform-browser": "21.2.13",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
+    "node_modules/@angular/platform-browser": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.13.tgz",
+      "integrity": "sha512-96rcwLHsklqAYRuS2SEBOUdQS5PLkuUIEEIjpYu4rxU2PVvOMapJEImM/QBxrbwjnCgRbj/CivkgfjiR0R0wSA==",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/animations": "21.2.13",
+        "@angular/common": "21.2.13",
+        "@angular/core": "21.2.13"
+      },
+      "peerDependenciesMeta": {
+        "@angular/animations": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/router": {
+      "version": "21.2.13",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.2.13.tgz",
+      "integrity": "sha512-/JXtdhUH/rDGiJmUNrrbs52Aji4sygVCz5HIBujrnj3cjreKam7n98Ufkh0aZvAKybdGd5A8srNUFePzAvfExQ==",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "21.2.13",
+        "@angular/core": "21.2.13",
+        "@angular/platform-browser": "21.2.13",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
+    "node_modules/@asamuzakjp/css-color": {
+      "version": "5.1.11",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
+      "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
+      "dev": true,
+      "dependencies": {
+        "@asamuzakjp/generational-cache": "^1.0.1",
+        "@csstools/css-calc": "^3.2.0",
+        "@csstools/css-color-parser": "^4.1.0",
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@asamuzakjp/dom-selector": {
+      "version": "6.8.1",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz",
+      "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==",
+      "dev": true,
+      "dependencies": {
+        "@asamuzakjp/nwsapi": "^2.3.9",
+        "bidi-js": "^1.0.3",
+        "css-tree": "^3.1.0",
+        "is-potential-custom-element-name": "^1.0.1",
+        "lru-cache": "^11.2.6"
+      }
+    },
+    "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@asamuzakjp/generational-cache": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
+      "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
+      "dev": true,
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@asamuzakjp/nwsapi": {
+      "version": "2.3.9",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
+      "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
+      "dev": true
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+      "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.28.5",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.29.3",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
+      "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+      "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.29.0",
+        "@babel/generator": "^7.29.0",
+        "@babel/helper-compilation-targets": "^7.28.6",
+        "@babel/helper-module-transforms": "^7.28.6",
+        "@babel/helpers": "^7.28.6",
+        "@babel/parser": "^7.29.0",
+        "@babel/template": "^7.28.6",
+        "@babel/traverse": "^7.29.0",
+        "@babel/types": "^7.29.0",
+        "@jridgewell/remapping": "^2.3.5",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/core/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/@babel/core/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/@babel/generator": {
+      "version": "7.29.1",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+      "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.29.0",
+        "@babel/types": "^7.29.0",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.27.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+      "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.27.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+      "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/compat-data": "^7.28.6",
+        "@babel/helper-validator-option": "^7.27.1",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/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/@babel/helper-globals": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+      "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+      "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/traverse": "^7.28.6",
+        "@babel/types": "^7.28.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+      "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.28.6",
+        "@babel/helper-validator-identifier": "^7.28.5",
+        "@babel/traverse": "^7.28.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-split-export-declaration": {
+      "version": "7.24.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
+      "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.24.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+      "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.29.2",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+      "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.28.6",
+        "@babel/types": "^7.29.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.29.3",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
+      "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.29.0"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+      "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.28.6",
+        "@babel/parser": "^7.28.6",
+        "@babel/types": "^7.28.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+      "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.29.0",
+        "@babel/generator": "^7.29.0",
+        "@babel/helper-globals": "^7.28.0",
+        "@babel/parser": "^7.29.0",
+        "@babel/template": "^7.28.6",
+        "@babel/types": "^7.29.0",
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+      "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@bramus/specificity": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
+      "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
+      "dev": true,
+      "dependencies": {
+        "css-tree": "^3.0.0"
+      },
+      "bin": {
+        "specificity": "bin/cli.js"
+      }
+    },
+    "node_modules/@csstools/color-helpers": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz",
+      "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    "node_modules/@csstools/css-calc": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz",
+      "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
+    },
+    "node_modules/@csstools/css-color-parser": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz",
+      "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "dependencies": {
+        "@csstools/color-helpers": "^6.0.2",
+        "@csstools/css-calc": "^3.2.1"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
+    },
+    "node_modules/@csstools/css-parser-algorithms": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
+      "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
+    },
+    "node_modules/@csstools/css-syntax-patches-for-csstree": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz",
+      "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "peerDependencies": {
+        "css-tree": "^3.2.1"
+      },
+      "peerDependenciesMeta": {
+        "css-tree": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@csstools/css-tokenizer": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
+      "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    "node_modules/@emnapi/core": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+      "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "@emnapi/wasi-threads": "1.2.1",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/runtime": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+      "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/wasi-threads": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+      "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+      "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+      "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+      "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+      "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+      "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+      "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+      "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+      "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+      "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+      "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+      "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+      "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+      "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+      "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+      "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+      "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+      "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+      "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+      "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+      "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+      "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+      "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+      "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+      "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+      "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+      "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@exodus/bytes": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz",
+      "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==",
+      "dev": true,
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@noble/hashes": "^1.8.0 || ^2.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@noble/hashes": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@gar/promise-retry": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz",
+      "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@harperfast/extended-iterable": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@harperfast/extended-iterable/-/extended-iterable-1.0.3.tgz",
+      "integrity": "sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/@hono/node-server": {
+      "version": "1.19.14",
+      "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz",
+      "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.14.1"
+      },
+      "peerDependencies": {
+        "hono": "^4"
+      }
+    },
+    "node_modules/@inquirer/ansi": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",
+      "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/checkbox": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz",
+      "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/confirm": {
+      "version": "5.1.21",
+      "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz",
+      "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/core": {
+      "version": "10.3.2",
+      "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz",
+      "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "cli-width": "^4.1.0",
+        "mute-stream": "^2.0.0",
+        "signal-exit": "^4.1.0",
+        "wrap-ansi": "^6.2.0",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/editor": {
+      "version": "4.2.23",
+      "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz",
+      "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/external-editor": "^1.0.3",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/expand": {
+      "version": "4.0.23",
+      "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz",
+      "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/external-editor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+      "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
+      "dev": true,
+      "dependencies": {
+        "chardet": "^2.1.1",
+        "iconv-lite": "^0.7.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/figures": {
+      "version": "1.0.15",
+      "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz",
+      "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/input": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz",
+      "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/number": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz",
+      "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/password": {
+      "version": "4.0.23",
+      "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz",
+      "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz",
+      "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/checkbox": "^4.3.2",
+        "@inquirer/confirm": "^5.1.21",
+        "@inquirer/editor": "^4.2.23",
+        "@inquirer/expand": "^4.0.23",
+        "@inquirer/input": "^4.3.1",
+        "@inquirer/number": "^3.0.23",
+        "@inquirer/password": "^4.0.23",
+        "@inquirer/rawlist": "^4.1.11",
+        "@inquirer/search": "^3.2.2",
+        "@inquirer/select": "^4.4.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/rawlist": {
+      "version": "4.1.11",
+      "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz",
+      "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/search": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz",
+      "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/select": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz",
+      "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/type": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz",
+      "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@isaacs/fs-minipass": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+      "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.0.4"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
+      "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/remapping": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "dev": true
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@listr2/prompt-adapter-inquirer": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz",
+      "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==",
+      "dev": true,
+      "dependencies": {
+        "@inquirer/type": "^3.0.8"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      },
+      "peerDependencies": {
+        "@inquirer/prompts": ">= 3 < 8",
+        "listr2": "9.0.5"
+      }
+    },
+    "node_modules/@lmdb/lmdb-darwin-arm64": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.1.tgz",
+      "integrity": "sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-darwin-x64": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.1.tgz",
+      "integrity": "sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.1.tgz",
+      "integrity": "sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm64": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.1.tgz",
+      "integrity": "sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-x64": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.1.tgz",
+      "integrity": "sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-win32-arm64": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.1.tgz",
+      "integrity": "sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-win32-x64": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.1.tgz",
+      "integrity": "sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@modelcontextprotocol/sdk": {
+      "version": "1.26.0",
+      "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz",
+      "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==",
+      "dev": true,
+      "dependencies": {
+        "@hono/node-server": "^1.19.9",
+        "ajv": "^8.17.1",
+        "ajv-formats": "^3.0.1",
+        "content-type": "^1.0.5",
+        "cors": "^2.8.5",
+        "cross-spawn": "^7.0.5",
+        "eventsource": "^3.0.2",
+        "eventsource-parser": "^3.0.0",
+        "express": "^5.2.1",
+        "express-rate-limit": "^8.2.1",
+        "hono": "^4.11.4",
+        "jose": "^6.1.3",
+        "json-schema-typed": "^8.0.2",
+        "pkce-challenge": "^5.0.0",
+        "raw-body": "^3.0.0",
+        "zod": "^3.25 || ^4.0",
+        "zod-to-json-schema": "^3.25.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@cfworker/json-schema": "^4.1.1",
+        "zod": "^3.25 || ^4.0"
+      },
+      "peerDependenciesMeta": {
+        "@cfworker/json-schema": {
+          "optional": true
+        },
+        "zod": {
+          "optional": false
+        }
+      }
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
+      "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
+      "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
+      "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
+      "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
+      "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
+      "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@napi-rs/nice": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz",
+      "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==",
+      "dev": true,
+      "optional": true,
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      },
+      "optionalDependencies": {
+        "@napi-rs/nice-android-arm-eabi": "1.1.1",
+        "@napi-rs/nice-android-arm64": "1.1.1",
+        "@napi-rs/nice-darwin-arm64": "1.1.1",
+        "@napi-rs/nice-darwin-x64": "1.1.1",
+        "@napi-rs/nice-freebsd-x64": "1.1.1",
+        "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1",
+        "@napi-rs/nice-linux-arm64-gnu": "1.1.1",
+        "@napi-rs/nice-linux-arm64-musl": "1.1.1",
+        "@napi-rs/nice-linux-ppc64-gnu": "1.1.1",
+        "@napi-rs/nice-linux-riscv64-gnu": "1.1.1",
+        "@napi-rs/nice-linux-s390x-gnu": "1.1.1",
+        "@napi-rs/nice-linux-x64-gnu": "1.1.1",
+        "@napi-rs/nice-linux-x64-musl": "1.1.1",
+        "@napi-rs/nice-openharmony-arm64": "1.1.1",
+        "@napi-rs/nice-win32-arm64-msvc": "1.1.1",
+        "@napi-rs/nice-win32-ia32-msvc": "1.1.1",
+        "@napi-rs/nice-win32-x64-msvc": "1.1.1"
+      }
+    },
+    "node_modules/@napi-rs/nice-android-arm-eabi": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz",
+      "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-android-arm64": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz",
+      "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-darwin-arm64": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz",
+      "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-darwin-x64": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz",
+      "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-freebsd-x64": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz",
+      "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-arm-gnueabihf": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz",
+      "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-arm64-gnu": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz",
+      "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-arm64-musl": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz",
+      "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-ppc64-gnu": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz",
+      "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-riscv64-gnu": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz",
+      "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-s390x-gnu": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz",
+      "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-x64-gnu": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz",
+      "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-linux-x64-musl": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz",
+      "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-openharmony-arm64": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz",
+      "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-win32-arm64-msvc": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz",
+      "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-win32-ia32-msvc": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz",
+      "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/nice-win32-x64-msvc": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz",
+      "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/wasm-runtime": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+      "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "@tybys/wasm-util": "^0.10.1"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      },
+      "peerDependencies": {
+        "@emnapi/core": "^1.7.1",
+        "@emnapi/runtime": "^1.7.1"
+      }
+    },
+    "node_modules/@npmcli/agent": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz",
+      "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==",
+      "dev": true,
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.1",
+        "lru-cache": "^11.2.1",
+        "socks-proxy-agent": "^8.0.3"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/agent/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@npmcli/fs": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz",
+      "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==",
+      "dev": true,
+      "dependencies": {
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/git": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz",
+      "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==",
+      "dev": true,
+      "dependencies": {
+        "@gar/promise-retry": "^1.0.0",
+        "@npmcli/promise-spawn": "^9.0.0",
+        "ini": "^6.0.0",
+        "lru-cache": "^11.2.1",
+        "npm-pick-manifest": "^11.0.1",
+        "proc-log": "^6.0.0",
+        "semver": "^7.3.5",
+        "which": "^6.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/git/node_modules/isexe": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
+      "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
+      "dev": true,
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/@npmcli/git/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@npmcli/git/node_modules/which": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
+      "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^4.0.0"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/installed-package-contents": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz",
+      "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==",
+      "dev": true,
+      "dependencies": {
+        "npm-bundled": "^5.0.0",
+        "npm-normalize-package-bin": "^5.0.0"
+      },
+      "bin": {
+        "installed-package-contents": "bin/index.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/node-gyp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz",
+      "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/package-json": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz",
+      "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==",
+      "dev": true,
+      "dependencies": {
+        "@npmcli/git": "^7.0.0",
+        "glob": "^13.0.0",
+        "hosted-git-info": "^9.0.0",
+        "json-parse-even-better-errors": "^5.0.0",
+        "proc-log": "^6.0.0",
+        "semver": "^7.5.3",
+        "spdx-expression-parse": "^4.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/promise-spawn": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz",
+      "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==",
+      "dev": true,
+      "dependencies": {
+        "which": "^6.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/promise-spawn/node_modules/isexe": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
+      "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
+      "dev": true,
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/@npmcli/promise-spawn/node_modules/which": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
+      "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^4.0.0"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/redact": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz",
+      "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@npmcli/run-script": {
+      "version": "10.0.4",
+      "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz",
+      "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==",
+      "dev": true,
+      "dependencies": {
+        "@npmcli/node-gyp": "^5.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "@npmcli/promise-spawn": "^9.0.0",
+        "node-gyp": "^12.1.0",
+        "proc-log": "^6.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@oxc-project/types": {
+      "version": "0.113.0",
+      "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.113.0.tgz",
+      "integrity": "sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/Boshen"
+      }
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
+      "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^2.0.3",
+        "is-glob": "^4.0.3",
+        "node-addon-api": "^7.0.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher-android-arm64": "2.5.6",
+        "@parcel/watcher-darwin-arm64": "2.5.6",
+        "@parcel/watcher-darwin-x64": "2.5.6",
+        "@parcel/watcher-freebsd-x64": "2.5.6",
+        "@parcel/watcher-linux-arm-glibc": "2.5.6",
+        "@parcel/watcher-linux-arm-musl": "2.5.6",
+        "@parcel/watcher-linux-arm64-glibc": "2.5.6",
+        "@parcel/watcher-linux-arm64-musl": "2.5.6",
+        "@parcel/watcher-linux-x64-glibc": "2.5.6",
+        "@parcel/watcher-linux-x64-musl": "2.5.6",
+        "@parcel/watcher-win32-arm64": "2.5.6",
+        "@parcel/watcher-win32-ia32": "2.5.6",
+        "@parcel/watcher-win32-x64": "2.5.6"
+      }
+    },
+    "node_modules/@parcel/watcher-android-arm64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
+      "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-arm64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
+      "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-x64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
+      "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-freebsd-x64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
+      "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
+      "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
+      "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
+      "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
+      "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
+      "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
+      "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-arm64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
+      "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-ia32": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
+      "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-x64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
+      "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher/node_modules/node-addon-api": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+      "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/@rolldown/binding-android-arm64": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.4.tgz",
+      "integrity": "sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-darwin-arm64": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.4.tgz",
+      "integrity": "sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-darwin-x64": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.4.tgz",
+      "integrity": "sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-freebsd-x64": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.4.tgz",
+      "integrity": "sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.4.tgz",
+      "integrity": "sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-linux-arm64-gnu": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.4.tgz",
+      "integrity": "sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-linux-arm64-musl": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.4.tgz",
+      "integrity": "sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-linux-x64-gnu": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.4.tgz",
+      "integrity": "sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-linux-x64-musl": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.4.tgz",
+      "integrity": "sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-openharmony-arm64": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.4.tgz",
+      "integrity": "sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-wasm32-wasi": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.4.tgz",
+      "integrity": "sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==",
+      "cpu": [
+        "wasm32"
+      ],
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "@napi-rs/wasm-runtime": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@rolldown/binding-win32-arm64-msvc": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.4.tgz",
+      "integrity": "sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/binding-win32-x64-msvc": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.4.tgz",
+      "integrity": "sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.4.tgz",
+      "integrity": "sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==",
+      "dev": true
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz",
+      "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz",
+      "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz",
+      "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz",
+      "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz",
+      "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz",
+      "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz",
+      "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz",
+      "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz",
+      "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz",
+      "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz",
+      "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-musl": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz",
+      "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz",
+      "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-musl": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz",
+      "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz",
+      "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz",
+      "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz",
+      "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz",
+      "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz",
+      "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openbsd-x64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz",
+      "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz",
+      "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz",
+      "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz",
+      "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz",
+      "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz",
+      "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@schematics/angular": {
+      "version": "21.2.11",
+      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.11.tgz",
+      "integrity": "sha512-EqH12Fr3vaWFpsilFDFXkxwMIidEDZr5cGl0w2hDRG7DjXE2oRB/VXix8xmpuHkzJ40Jgew6hIc+bfbwQhFK1A==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "21.2.11",
+        "@angular-devkit/schematics": "21.2.11",
+        "jsonc-parser": "3.3.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@sigstore/bundle": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz",
+      "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==",
+      "dev": true,
+      "dependencies": {
+        "@sigstore/protobuf-specs": "^0.5.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@sigstore/core": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.0.tgz",
+      "integrity": "sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@sigstore/protobuf-specs": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.1.tgz",
+      "integrity": "sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==",
+      "dev": true,
+      "engines": {
+        "node": "^18.17.0 || >=20.5.0"
+      }
+    },
+    "node_modules/@sigstore/sign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz",
+      "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==",
+      "dev": true,
+      "dependencies": {
+        "@gar/promise-retry": "^1.0.2",
+        "@sigstore/bundle": "^4.0.0",
+        "@sigstore/core": "^3.2.0",
+        "@sigstore/protobuf-specs": "^0.5.0",
+        "make-fetch-happen": "^15.0.4",
+        "proc-log": "^6.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@sigstore/tuf": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz",
+      "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==",
+      "dev": true,
+      "dependencies": {
+        "@sigstore/protobuf-specs": "^0.5.0",
+        "tuf-js": "^4.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@sigstore/verify": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz",
+      "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==",
+      "dev": true,
+      "dependencies": {
+        "@sigstore/bundle": "^4.0.0",
+        "@sigstore/core": "^3.1.0",
+        "@sigstore/protobuf-specs": "^0.5.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@standard-schema/spec": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+      "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="
+    },
+    "node_modules/@stripe/stripe-js": {
+      "version": "9.8.0",
+      "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.8.0.tgz",
+      "integrity": "sha512-DHJpol/98VKyojNSYmpkB5vOMnlf87hPe0wPxyaYTNiTMk5QjKMXDfSZLwGctYIXAgAWDFeRABc8lFAj0BELyw==",
+      "engines": {
+        "node": ">=12.16"
+      }
+    },
+    "node_modules/@tufjs/canonical-json": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
+      "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==",
+      "dev": true,
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@tufjs/models": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz",
+      "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==",
+      "dev": true,
+      "dependencies": {
+        "@tufjs/canonical-json": "2.0.0",
+        "minimatch": "^10.1.1"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@tybys/wasm-util": {
+      "version": "0.10.2",
+      "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+      "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@types/chai": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+      "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+      "dev": true,
+      "dependencies": {
+        "@types/deep-eql": "*",
+        "assertion-error": "^2.0.1"
+      }
+    },
+    "node_modules/@types/deep-eql": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+      "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+      "dev": true
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true
+    },
+    "node_modules/@vitejs/plugin-basic-ssl": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz",
+      "integrity": "sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==",
+      "dev": true,
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^6.0.0 || ^7.0.0"
+      }
+    },
+    "node_modules/@vitest/expect": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz",
+      "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==",
+      "dev": true,
+      "dependencies": {
+        "@standard-schema/spec": "^1.1.0",
+        "@types/chai": "^5.2.2",
+        "@vitest/spy": "4.1.6",
+        "@vitest/utils": "4.1.6",
+        "chai": "^6.2.2",
+        "tinyrainbow": "^3.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/mocker": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz",
+      "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/spy": "4.1.6",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.21"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz",
+      "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==",
+      "dev": true,
+      "dependencies": {
+        "tinyrainbow": "^3.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz",
+      "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/utils": "4.1.6",
+        "pathe": "^2.0.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz",
+      "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/pretty-format": "4.1.6",
+        "@vitest/utils": "4.1.6",
+        "magic-string": "^0.30.21",
+        "pathe": "^2.0.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz",
+      "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==",
+      "dev": true,
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz",
+      "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/pretty-format": "4.1.6",
+        "convert-source-map": "^2.0.0",
+        "tinyrainbow": "^3.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils/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/@yarnpkg/lockfile": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+      "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+      "dev": true
+    },
+    "node_modules/abbrev": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz",
+      "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+      "dev": true,
+      "dependencies": {
+        "mime-types": "^3.0.0",
+        "negotiator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+      "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/algoliasearch": {
+      "version": "5.48.1",
+      "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.48.1.tgz",
+      "integrity": "sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==",
+      "dev": true,
+      "dependencies": {
+        "@algolia/abtesting": "1.14.1",
+        "@algolia/client-abtesting": "5.48.1",
+        "@algolia/client-analytics": "5.48.1",
+        "@algolia/client-common": "5.48.1",
+        "@algolia/client-insights": "5.48.1",
+        "@algolia/client-personalization": "5.48.1",
+        "@algolia/client-query-suggestions": "5.48.1",
+        "@algolia/client-search": "5.48.1",
+        "@algolia/ingestion": "1.48.1",
+        "@algolia/monitoring": "1.48.1",
+        "@algolia/recommend": "5.48.1",
+        "@algolia/requester-browser-xhr": "5.48.1",
+        "@algolia/requester-fetch": "5.48.1",
+        "@algolia/requester-node-http": "5.48.1"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+      "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+      "dev": true,
+      "dependencies": {
+        "environment": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "dev": true,
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.10.30",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz",
+      "integrity": "sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg==",
+      "dev": true,
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.cjs"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/beasties": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.4.1.tgz",
+      "integrity": "sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==",
+      "dev": true,
+      "dependencies": {
+        "css-select": "^6.0.0",
+        "css-what": "^7.0.0",
+        "dom-serializer": "^2.0.0",
+        "domhandler": "^5.0.3",
+        "htmlparser2": "^10.0.0",
+        "picocolors": "^1.1.1",
+        "postcss": "^8.4.49",
+        "postcss-media-query-parser": "^0.2.3",
+        "postcss-safe-parser": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/bidi-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
+      "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
+      "dev": true,
+      "dependencies": {
+        "require-from-string": "^2.0.2"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+      "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
+      "dev": true,
+      "dependencies": {
+        "bytes": "^3.1.2",
+        "content-type": "^1.0.5",
+        "debug": "^4.4.3",
+        "http-errors": "^2.0.0",
+        "iconv-lite": "^0.7.0",
+        "on-finished": "^2.4.1",
+        "qs": "^6.14.1",
+        "raw-body": "^3.0.1",
+        "type-is": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true
+    },
+    "node_modules/brace-expansion": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+      "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+      "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": {
+        "baseline-browser-mapping": "^2.10.12",
+        "caniuse-lite": "^1.0.30001782",
+        "electron-to-chromium": "^1.5.328",
+        "node-releases": "^2.0.36",
+        "update-browserslist-db": "^1.2.3"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/cacache": {
+      "version": "20.0.4",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz",
+      "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==",
+      "dev": true,
+      "dependencies": {
+        "@npmcli/fs": "^5.0.0",
+        "fs-minipass": "^3.0.0",
+        "glob": "^13.0.0",
+        "lru-cache": "^11.1.0",
+        "minipass": "^7.0.3",
+        "minipass-collect": "^2.0.1",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "p-map": "^7.0.2",
+        "ssri": "^13.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/cacache/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001793",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
+      "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
+      "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/chai": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+      "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chardet": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
+      "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+      "dev": true
+    },
+    "node_modules/chokidar": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+      "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+      "dev": true,
+      "dependencies": {
+        "readdirp": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/chownr": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+      "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+      "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+      "dev": true,
+      "dependencies": {
+        "restore-cursor": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-spinners": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
+      "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
+      "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
+      "dev": true,
+      "dependencies": {
+        "slice-ansi": "^8.0.0",
+        "string-width": "^8.2.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-width": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+      "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+      "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^7.2.0",
+        "strip-ansi": "^7.1.0",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/cliui/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cliui/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?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/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true
+    },
+    "node_modules/content-disposition": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
+      "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.6.0"
+      }
+    },
+    "node_modules/cors": {
+      "version": "2.8.6",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+      "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+      "dev": true,
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "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/css-select": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz",
+      "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^7.0.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.2.2",
+        "nth-check": "^2.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-tree": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+      "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+      "dev": true,
+      "dependencies": {
+        "mdn-data": "2.27.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz",
+      "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/cssstyle": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz",
+      "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==",
+      "dev": true,
+      "dependencies": {
+        "@asamuzakjp/css-color": "^5.0.1",
+        "@csstools/css-syntax-patches-for-csstree": "^1.0.28",
+        "css-tree": "^3.1.0",
+        "lru-cache": "^11.2.6"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/cssstyle/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/data-urls": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
+      "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==",
+      "dev": true,
+      "dependencies": {
+        "whatwg-mimetype": "^5.0.0",
+        "whatwg-url": "^16.0.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decimal.js": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+      "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+      "dev": true
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "dev": true,
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+      "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.2",
+        "entities": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
+    "node_modules/domhandler": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+      "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": "^2.3.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+      "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+      "dev": true,
+      "dependencies": {
+        "dom-serializer": "^2.0.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dev": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "dev": true
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.357",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.357.tgz",
+      "integrity": "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==",
+      "dev": true
+    },
+    "node_modules/emoji-regex": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+      "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+      "dev": true
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/environment": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+      "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/err-code": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+      "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+      "dev": true
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
+      "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
+      "dev": true
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+      "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.27.3",
+        "@esbuild/android-arm": "0.27.3",
+        "@esbuild/android-arm64": "0.27.3",
+        "@esbuild/android-x64": "0.27.3",
+        "@esbuild/darwin-arm64": "0.27.3",
+        "@esbuild/darwin-x64": "0.27.3",
+        "@esbuild/freebsd-arm64": "0.27.3",
+        "@esbuild/freebsd-x64": "0.27.3",
+        "@esbuild/linux-arm": "0.27.3",
+        "@esbuild/linux-arm64": "0.27.3",
+        "@esbuild/linux-ia32": "0.27.3",
+        "@esbuild/linux-loong64": "0.27.3",
+        "@esbuild/linux-mips64el": "0.27.3",
+        "@esbuild/linux-ppc64": "0.27.3",
+        "@esbuild/linux-riscv64": "0.27.3",
+        "@esbuild/linux-s390x": "0.27.3",
+        "@esbuild/linux-x64": "0.27.3",
+        "@esbuild/netbsd-arm64": "0.27.3",
+        "@esbuild/netbsd-x64": "0.27.3",
+        "@esbuild/openbsd-arm64": "0.27.3",
+        "@esbuild/openbsd-x64": "0.27.3",
+        "@esbuild/openharmony-arm64": "0.27.3",
+        "@esbuild/sunos-x64": "0.27.3",
+        "@esbuild/win32-arm64": "0.27.3",
+        "@esbuild/win32-ia32": "0.27.3",
+        "@esbuild/win32-x64": "0.27.3"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "dev": true
+    },
+    "node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+      "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+      "dev": true
+    },
+    "node_modules/eventsource": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+      "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+      "dev": true,
+      "dependencies": {
+        "eventsource-parser": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/eventsource-parser": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz",
+      "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/expect-type": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+      "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/exponential-backoff": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
+      "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
+      "dev": true
+    },
+    "node_modules/express": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+      "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+      "dev": true,
+      "dependencies": {
+        "accepts": "^2.0.0",
+        "body-parser": "^2.2.1",
+        "content-disposition": "^1.0.0",
+        "content-type": "^1.0.5",
+        "cookie": "^0.7.1",
+        "cookie-signature": "^1.2.1",
+        "debug": "^4.4.0",
+        "depd": "^2.0.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "finalhandler": "^2.1.0",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "merge-descriptors": "^2.0.0",
+        "mime-types": "^3.0.0",
+        "on-finished": "^2.4.1",
+        "once": "^1.4.0",
+        "parseurl": "^1.3.3",
+        "proxy-addr": "^2.0.7",
+        "qs": "^6.14.0",
+        "range-parser": "^1.2.1",
+        "router": "^2.2.0",
+        "send": "^1.1.0",
+        "serve-static": "^2.2.0",
+        "statuses": "^2.0.1",
+        "type-is": "^2.0.1",
+        "vary": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/express-rate-limit": {
+      "version": "8.5.2",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz",
+      "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==",
+      "dev": true,
+      "dependencies": {
+        "ip-address": "^10.2.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/express-rate-limit"
+      },
+      "peerDependencies": {
+        "express": ">= 4.11"
+      }
+    },
+    "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-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
+      "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ]
+    },
+    "node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+      "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "on-finished": "^2.4.1",
+        "parseurl": "^1.3.3",
+        "statuses": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/fs-minipass": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
+      "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.0.3"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "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/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "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/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-east-asian-width": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+      "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dev": true,
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/glob": {
+      "version": "13.0.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+      "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+      "dev": true,
+      "dependencies": {
+        "minimatch": "^10.2.2",
+        "minipass": "^7.1.3",
+        "path-scurry": "^2.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+      "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hono": {
+      "version": "4.12.19",
+      "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.19.tgz",
+      "integrity": "sha512-xa3eYXYXx68XTT4hZ7dRzsXBhaq85ToSrlUJNoR0gwz/1Ap/CNwX47wfvV7pc/xWhjKVVkLT7zBJy8chhNguqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.9.0"
+      }
+    },
+    "node_modules/hosted-git-info": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz",
+      "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^11.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/hosted-git-info/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/html-encoding-sniffer": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
+      "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
+      "dev": true,
+      "dependencies": {
+        "@exodus/bytes": "^1.6.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
+      "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
+      "dev": true,
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.2.2",
+        "entities": "^7.0.1"
+      }
+    },
+    "node_modules/htmlparser2/node_modules/entities": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/http-cache-semantics": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+      "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+      "dev": true
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "dev": true,
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+      "dev": true,
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+      "dev": true,
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+      "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+      "dev": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/ignore-walk": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz",
+      "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==",
+      "dev": true,
+      "dependencies": {
+        "minimatch": "^10.0.3"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz",
+      "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==",
+      "dev": true
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/ini": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz",
+      "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/ip-address": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
+      "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "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,
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+      "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+      "dev": true,
+      "dependencies": {
+        "get-east-asian-width": "^1.3.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "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,
+      "optional": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+      "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-potential-custom-element-name": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+      "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+      "dev": true
+    },
+    "node_modules/is-promise": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+      "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+      "dev": true
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+      "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "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/istanbul-lib-coverage": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-instrument": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+      "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.23.9",
+        "@babel/parser": "^7.23.9",
+        "@istanbuljs/schema": "^0.1.3",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jose": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz",
+      "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/panva"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "node_modules/jsdom": {
+      "version": "28.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz",
+      "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==",
+      "dev": true,
+      "dependencies": {
+        "@acemir/cssom": "^0.9.31",
+        "@asamuzakjp/dom-selector": "^6.8.1",
+        "@bramus/specificity": "^2.4.2",
+        "@exodus/bytes": "^1.11.0",
+        "cssstyle": "^6.0.1",
+        "data-urls": "^7.0.0",
+        "decimal.js": "^10.6.0",
+        "html-encoding-sniffer": "^6.0.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.6",
+        "is-potential-custom-element-name": "^1.0.1",
+        "parse5": "^8.0.0",
+        "saxes": "^6.0.0",
+        "symbol-tree": "^3.2.4",
+        "tough-cookie": "^6.0.0",
+        "undici": "^7.21.0",
+        "w3c-xmlserializer": "^5.0.0",
+        "webidl-conversions": "^8.0.1",
+        "whatwg-mimetype": "^5.0.0",
+        "whatwg-url": "^16.0.0",
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "canvas": "^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "canvas": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz",
+      "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true
+    },
+    "node_modules/json-schema-typed": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
+      "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
+      "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/jsonc-parser": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+      "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+      "dev": true
+    },
+    "node_modules/jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+      "dev": true,
+      "engines": [
+        "node >= 0.2.0"
+      ]
+    },
+    "node_modules/listr2": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
+      "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
+      "dev": true,
+      "dependencies": {
+        "cli-truncate": "^5.0.0",
+        "colorette": "^2.0.20",
+        "eventemitter3": "^5.0.1",
+        "log-update": "^6.1.0",
+        "rfdc": "^1.4.1",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
+    "node_modules/listr2/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/listr2/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/lmdb": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.1.tgz",
+      "integrity": "sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "dependencies": {
+        "@harperfast/extended-iterable": "^1.0.3",
+        "msgpackr": "^1.11.2",
+        "node-addon-api": "^6.1.0",
+        "node-gyp-build-optional-packages": "5.2.2",
+        "ordered-binary": "^1.5.3",
+        "weak-lru-cache": "^1.2.2"
+      },
+      "bin": {
+        "download-lmdb-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@lmdb/lmdb-darwin-arm64": "3.5.1",
+        "@lmdb/lmdb-darwin-x64": "3.5.1",
+        "@lmdb/lmdb-linux-arm": "3.5.1",
+        "@lmdb/lmdb-linux-arm64": "3.5.1",
+        "@lmdb/lmdb-linux-x64": "3.5.1",
+        "@lmdb/lmdb-win32-arm64": "3.5.1",
+        "@lmdb/lmdb-win32-x64": "3.5.1"
+      }
+    },
+    "node_modules/log-symbols": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz",
+      "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
+      "dev": true,
+      "dependencies": {
+        "is-unicode-supported": "^2.0.0",
+        "yoctocolors": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+      "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+      "dev": true,
+      "dependencies": {
+        "ansi-escapes": "^7.0.0",
+        "cli-cursor": "^5.0.0",
+        "slice-ansi": "^7.1.0",
+        "strip-ansi": "^7.1.0",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/slice-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+      "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "is-fullwidth-code-point": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "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/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/make-fetch-happen": {
+      "version": "15.0.5",
+      "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz",
+      "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==",
+      "dev": true,
+      "dependencies": {
+        "@gar/promise-retry": "^1.0.0",
+        "@npmcli/agent": "^4.0.0",
+        "@npmcli/redact": "^4.0.0",
+        "cacache": "^20.0.1",
+        "http-cache-semantics": "^4.1.1",
+        "minipass": "^7.0.2",
+        "minipass-fetch": "^5.0.0",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "negotiator": "^1.0.0",
+        "proc-log": "^6.0.0",
+        "ssri": "^13.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mdn-data": {
+      "version": "2.27.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+      "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+      "dev": true
+    },
+    "node_modules/media-typer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+      "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+      "dev": true,
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/mimic-function": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+      "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^5.0.5"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+      "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+      "dev": true,
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/minipass-collect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz",
+      "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.0.3"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/minipass-fetch": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz",
+      "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.0.3",
+        "minipass-sized": "^2.0.0",
+        "minizlib": "^3.0.1"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      },
+      "optionalDependencies": {
+        "iconv-lite": "^0.7.2"
+      }
+    },
+    "node_modules/minipass-flush": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz",
+      "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minipass-flush/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-flush/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "node_modules/minipass-pipeline": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-pipeline/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-pipeline/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "node_modules/minipass-sized": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz",
+      "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.1.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minizlib": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+      "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.1.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/mrmime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+      "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "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/msgpackr": {
+      "version": "1.11.12",
+      "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.12.tgz",
+      "integrity": "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==",
+      "dev": true,
+      "optional": true,
+      "optionalDependencies": {
+        "msgpackr-extract": "^3.0.2"
+      }
+    },
+    "node_modules/msgpackr-extract": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
+      "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "dependencies": {
+        "node-gyp-build-optional-packages": "5.2.2"
+      },
+      "bin": {
+        "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
+      }
+    },
+    "node_modules/mute-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
+      "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
+      "dev": true,
+      "engines": {
+        "node": "^18.17.0 || >=20.5.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.12",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+      "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+      "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/negotiator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
+      "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/node-gyp": {
+      "version": "12.3.0",
+      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz",
+      "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==",
+      "dev": true,
+      "dependencies": {
+        "env-paths": "^2.2.0",
+        "exponential-backoff": "^3.1.1",
+        "graceful-fs": "^4.2.6",
+        "nopt": "^9.0.0",
+        "proc-log": "^6.0.0",
+        "semver": "^7.3.5",
+        "tar": "^7.5.4",
+        "tinyglobby": "^0.2.12",
+        "undici": "^6.25.0",
+        "which": "^6.0.0"
+      },
+      "bin": {
+        "node-gyp": "bin/node-gyp.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/node-gyp-build-optional-packages": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
+      "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^2.0.1"
+      },
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/node-gyp/node_modules/isexe": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
+      "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
+      "dev": true,
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/node-gyp/node_modules/undici": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
+      "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.17"
+      }
+    },
+    "node_modules/node-gyp/node_modules/which": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
+      "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^4.0.0"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.44",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz",
+      "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==",
+      "dev": true
+    },
+    "node_modules/nopt": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
+      "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==",
+      "dev": true,
+      "dependencies": {
+        "abbrev": "^4.0.0"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-bundled": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz",
+      "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==",
+      "dev": true,
+      "dependencies": {
+        "npm-normalize-package-bin": "^5.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-install-checks": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz",
+      "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==",
+      "dev": true,
+      "dependencies": {
+        "semver": "^7.1.1"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-normalize-package-bin": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz",
+      "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-package-arg": {
+      "version": "13.0.2",
+      "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz",
+      "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==",
+      "dev": true,
+      "dependencies": {
+        "hosted-git-info": "^9.0.0",
+        "proc-log": "^6.0.0",
+        "semver": "^7.3.5",
+        "validate-npm-package-name": "^7.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-packlist": {
+      "version": "10.0.4",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz",
+      "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==",
+      "dev": true,
+      "dependencies": {
+        "ignore-walk": "^8.0.0",
+        "proc-log": "^6.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-pick-manifest": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz",
+      "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==",
+      "dev": true,
+      "dependencies": {
+        "npm-install-checks": "^8.0.0",
+        "npm-normalize-package-bin": "^5.0.0",
+        "npm-package-arg": "^13.0.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm-registry-fetch": {
+      "version": "19.1.1",
+      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz",
+      "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==",
+      "dev": true,
+      "dependencies": {
+        "@npmcli/redact": "^4.0.0",
+        "jsonparse": "^1.3.1",
+        "make-fetch-happen": "^15.0.0",
+        "minipass": "^7.0.2",
+        "minipass-fetch": "^5.0.0",
+        "minizlib": "^3.0.1",
+        "npm-package-arg": "^13.0.0",
+        "proc-log": "^6.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/obug": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+      "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+      "dev": true,
+      "funding": [
+        "https://github.com/sponsors/sxzz",
+        "https://opencollective.com/debug"
+      ]
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "dev": true,
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+      "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+      "dev": true,
+      "dependencies": {
+        "mimic-function": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ora": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz",
+      "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^5.6.2",
+        "cli-cursor": "^5.0.0",
+        "cli-spinners": "^3.2.0",
+        "is-interactive": "^2.0.0",
+        "is-unicode-supported": "^2.1.0",
+        "log-symbols": "^7.0.1",
+        "stdin-discarder": "^0.3.1",
+        "string-width": "^8.1.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ordered-binary": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz",
+      "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/p-map": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
+      "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/pacote": {
+      "version": "21.3.1",
+      "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.3.1.tgz",
+      "integrity": "sha512-O0EDXi85LF4AzdjG74GUwEArhdvawi/YOHcsW6IijKNj7wm8IvEWNF5GnfuxNpQ/ZpO3L37+v8hqdVh8GgWYhg==",
+      "dev": true,
+      "dependencies": {
+        "@npmcli/git": "^7.0.0",
+        "@npmcli/installed-package-contents": "^4.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "@npmcli/promise-spawn": "^9.0.0",
+        "@npmcli/run-script": "^10.0.0",
+        "cacache": "^20.0.0",
+        "fs-minipass": "^3.0.0",
+        "minipass": "^7.0.2",
+        "npm-package-arg": "^13.0.0",
+        "npm-packlist": "^10.0.1",
+        "npm-pick-manifest": "^11.0.1",
+        "npm-registry-fetch": "^19.0.0",
+        "proc-log": "^6.0.0",
+        "promise-retry": "^2.0.1",
+        "sigstore": "^4.0.0",
+        "ssri": "^13.0.0",
+        "tar": "^7.4.3"
+      },
+      "bin": {
+        "pacote": "bin/index.js"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
+      "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
+      "dev": true,
+      "dependencies": {
+        "entities": "^8.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-html-rewriting-stream": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz",
+      "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==",
+      "dev": true,
+      "dependencies": {
+        "entities": "^6.0.0",
+        "parse5": "^8.0.0",
+        "parse5-sax-parser": "^8.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-html-rewriting-stream/node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/parse5-sax-parser": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz",
+      "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==",
+      "dev": true,
+      "dependencies": {
+        "parse5": "^8.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5/node_modules/entities": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
+      "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
+      "dev": true,
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.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/path-scurry": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+      "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^11.0.0",
+        "minipass": "^7.1.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-scurry/node_modules/lru-cache": {
+      "version": "11.4.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+      "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+      "dev": true,
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "8.4.2",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+      "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
+      "dev": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true
+    },
+    "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": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/piscina": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.1.4.tgz",
+      "integrity": "sha512-7uU4ZnKeQq22t9AsmHGD2w4OYQGonwFnTypDypaWi7Qr2EvQIFVtG8J5D/3bE7W123Wdc9+v4CZDu5hJXVCtBg==",
+      "dev": true,
+      "engines": {
+        "node": ">=20.x"
+      },
+      "optionalDependencies": {
+        "@napi-rs/nice": "^1.0.4"
+      }
+    },
+    "node_modules/pkce-challenge": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
+      "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.14",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
+      "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
+      "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.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-media-query-parser": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
+      "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==",
+      "dev": true
+    },
+    "node_modules/postcss-safe-parser": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
+      "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "engines": {
+        "node": ">=18.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.4.31"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
+      "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
+      "dev": true,
+      "bin": {
+        "prettier": "bin/prettier.cjs"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/proc-log": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz",
+      "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/promise-retry": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+      "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+      "dev": true,
+      "dependencies": {
+        "err-code": "^2.0.2",
+        "retry": "^0.12.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "dev": true,
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "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/qs": {
+      "version": "6.15.2",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
+      "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+      "dev": true,
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+      "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+      "dev": true,
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.7.0",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+      "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/reflect-metadata": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+      "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+      "dev": true
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/restore-cursor": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+      "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+      "dev": true,
+      "dependencies": {
+        "onetime": "^7.0.0",
+        "signal-exit": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "dev": true
+    },
+    "node_modules/rolldown": {
+      "version": "1.0.0-rc.4",
+      "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.4.tgz",
+      "integrity": "sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==",
+      "dev": true,
+      "dependencies": {
+        "@oxc-project/types": "=0.113.0",
+        "@rolldown/pluginutils": "1.0.0-rc.4"
+      },
+      "bin": {
+        "rolldown": "bin/cli.mjs"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "optionalDependencies": {
+        "@rolldown/binding-android-arm64": "1.0.0-rc.4",
+        "@rolldown/binding-darwin-arm64": "1.0.0-rc.4",
+        "@rolldown/binding-darwin-x64": "1.0.0-rc.4",
+        "@rolldown/binding-freebsd-x64": "1.0.0-rc.4",
+        "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.4",
+        "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.4",
+        "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.4",
+        "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.4",
+        "@rolldown/binding-linux-x64-musl": "1.0.0-rc.4",
+        "@rolldown/binding-openharmony-arm64": "1.0.0-rc.4",
+        "@rolldown/binding-wasm32-wasi": "1.0.0-rc.4",
+        "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.4",
+        "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.4"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.60.4",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz",
+      "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.60.4",
+        "@rollup/rollup-android-arm64": "4.60.4",
+        "@rollup/rollup-darwin-arm64": "4.60.4",
+        "@rollup/rollup-darwin-x64": "4.60.4",
+        "@rollup/rollup-freebsd-arm64": "4.60.4",
+        "@rollup/rollup-freebsd-x64": "4.60.4",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.60.4",
+        "@rollup/rollup-linux-arm-musleabihf": "4.60.4",
+        "@rollup/rollup-linux-arm64-gnu": "4.60.4",
+        "@rollup/rollup-linux-arm64-musl": "4.60.4",
+        "@rollup/rollup-linux-loong64-gnu": "4.60.4",
+        "@rollup/rollup-linux-loong64-musl": "4.60.4",
+        "@rollup/rollup-linux-ppc64-gnu": "4.60.4",
+        "@rollup/rollup-linux-ppc64-musl": "4.60.4",
+        "@rollup/rollup-linux-riscv64-gnu": "4.60.4",
+        "@rollup/rollup-linux-riscv64-musl": "4.60.4",
+        "@rollup/rollup-linux-s390x-gnu": "4.60.4",
+        "@rollup/rollup-linux-x64-gnu": "4.60.4",
+        "@rollup/rollup-linux-x64-musl": "4.60.4",
+        "@rollup/rollup-openbsd-x64": "4.60.4",
+        "@rollup/rollup-openharmony-arm64": "4.60.4",
+        "@rollup/rollup-win32-arm64-msvc": "4.60.4",
+        "@rollup/rollup-win32-ia32-msvc": "4.60.4",
+        "@rollup/rollup-win32-x64-gnu": "4.60.4",
+        "@rollup/rollup-win32-x64-msvc": "4.60.4",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/router": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+      "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.4.0",
+        "depd": "^2.0.0",
+        "is-promise": "^4.0.0",
+        "parseurl": "^1.3.3",
+        "path-to-regexp": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "node_modules/sass": {
+      "version": "1.97.3",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz",
+      "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": "^4.0.0",
+        "immutable": "^5.0.2",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher": "^2.4.1"
+      }
+    },
+    "node_modules/sass/node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/sass/node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/saxes": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+      "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+      "dev": true,
+      "dependencies": {
+        "xmlchars": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=v12.22.7"
+      }
+    },
+    "node_modules/semver": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.4.3",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.1",
+        "mime-types": "^3.0.2",
+        "ms": "^2.1.3",
+        "on-finished": "^2.4.1",
+        "range-parser": "^1.2.1",
+        "statuses": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/serve-static": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+      "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+      "dev": true,
+      "dependencies": {
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "parseurl": "^1.3.3",
+        "send": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "dev": true
+    },
+    "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/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "dev": true,
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "dev": true,
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/sigstore": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz",
+      "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==",
+      "dev": true,
+      "dependencies": {
+        "@sigstore/bundle": "^4.0.0",
+        "@sigstore/core": "^3.1.0",
+        "@sigstore/protobuf-specs": "^0.5.0",
+        "@sigstore/sign": "^4.1.0",
+        "@sigstore/tuf": "^4.0.1",
+        "@sigstore/verify": "^3.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/slice-ansi": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
+      "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.2.3",
+        "is-fullwidth-code-point": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
+      "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
+      "dev": true,
+      "dependencies": {
+        "ip-address": "^10.1.1",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks-proxy-agent": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+      "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+      "dev": true,
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "socks": "^2.8.3"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.7.6",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+      "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "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/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/source-map-support/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/spdx-exceptions": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+      "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
+      "dev": true
+    },
+    "node_modules/spdx-expression-parse": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
+      "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
+      "dev": true,
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
+      "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
+      "dev": true
+    },
+    "node_modules/ssri": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz",
+      "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==",
+      "dev": true,
+      "dependencies": {
+        "minipass": "^7.0.3"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true
+    },
+    "node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/std-env": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+      "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
+      "dev": true
+    },
+    "node_modules/stdin-discarder": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz",
+      "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
+      "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
+      "dev": true,
+      "dependencies": {
+        "get-east-asian-width": "^1.5.0",
+        "strip-ansi": "^7.1.2"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/symbol-tree": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+      "dev": true
+    },
+    "node_modules/tar": {
+      "version": "7.5.15",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz",
+      "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==",
+      "dev": true,
+      "dependencies": {
+        "@isaacs/fs-minipass": "^4.0.0",
+        "chownr": "^3.0.0",
+        "minipass": "^7.1.2",
+        "minizlib": "^3.1.0",
+        "yallist": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tar/node_modules/yallist": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+      "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "dev": true
+    },
+    "node_modules/tinyexec": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz",
+      "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.15",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+      "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+      "dev": true,
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+      "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tldts": {
+      "version": "7.0.30",
+      "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz",
+      "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==",
+      "dev": true,
+      "dependencies": {
+        "tldts-core": "^7.0.30"
+      },
+      "bin": {
+        "tldts": "bin/cli.js"
+      }
+    },
+    "node_modules/tldts-core": {
+      "version": "7.0.30",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz",
+      "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==",
+      "dev": true
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/tough-cookie": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
+      "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
+      "dev": true,
+      "dependencies": {
+        "tldts": "^7.0.5"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
+      "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+    },
+    "node_modules/tuf-js": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz",
+      "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==",
+      "dev": true,
+      "dependencies": {
+        "@tufjs/models": "4.1.0",
+        "debug": "^4.4.3",
+        "make-fetch-happen": "^15.0.1"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
+      "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
+      "dev": true,
+      "dependencies": {
+        "content-type": "^2.0.0",
+        "media-typer": "^1.1.0",
+        "mime-types": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/type-is/node_modules/content-type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+      "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/undici": {
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz",
+      "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==",
+      "dev": true,
+      "engines": {
+        "node": ">=20.18.1"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+      "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/validate-npm-package-name": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz",
+      "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==",
+      "dev": true,
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vite": {
+      "version": "7.3.2",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
+      "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.27.0",
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3",
+        "postcss": "^8.5.6",
+        "rollup": "^4.43.0",
+        "tinyglobby": "^0.2.15"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^20.19.0 || >=22.12.0",
+        "jiti": ">=1.21.0",
+        "less": "^4.0.0",
+        "lightningcss": "^1.21.0",
+        "sass": "^1.70.0",
+        "sass-embedded": "^1.70.0",
+        "stylus": ">=0.54.8",
+        "sugarss": "^5.0.0",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vitest": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz",
+      "integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/expect": "4.1.6",
+        "@vitest/mocker": "4.1.6",
+        "@vitest/pretty-format": "4.1.6",
+        "@vitest/runner": "4.1.6",
+        "@vitest/snapshot": "4.1.6",
+        "@vitest/spy": "4.1.6",
+        "@vitest/utils": "4.1.6",
+        "es-module-lexer": "^2.0.0",
+        "expect-type": "^1.3.0",
+        "magic-string": "^0.30.21",
+        "obug": "^2.1.1",
+        "pathe": "^2.0.3",
+        "picomatch": "^4.0.3",
+        "std-env": "^4.0.0-rc.1",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^1.0.2",
+        "tinyglobby": "^0.2.15",
+        "tinyrainbow": "^3.1.0",
+        "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@opentelemetry/api": "^1.9.0",
+        "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+        "@vitest/browser-playwright": "4.1.6",
+        "@vitest/browser-preview": "4.1.6",
+        "@vitest/browser-webdriverio": "4.1.6",
+        "@vitest/coverage-istanbul": "4.1.6",
+        "@vitest/coverage-v8": "4.1.6",
+        "@vitest/ui": "4.1.6",
+        "happy-dom": "*",
+        "jsdom": "*",
+        "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@opentelemetry/api": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser-playwright": {
+          "optional": true
+        },
+        "@vitest/browser-preview": {
+          "optional": true
+        },
+        "@vitest/browser-webdriverio": {
+          "optional": true
+        },
+        "@vitest/coverage-istanbul": {
+          "optional": true
+        },
+        "@vitest/coverage-v8": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        },
+        "vite": {
+          "optional": false
+        }
+      }
+    },
+    "node_modules/w3c-xmlserializer": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+      "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+      "dev": true,
+      "dependencies": {
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/watchpack": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
+      "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
+      "dev": true,
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/weak-lru-cache": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
+      "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/webidl-conversions": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
+      "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/whatwg-mimetype": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
+      "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
+      "dev": true,
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "16.0.1",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz",
+      "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==",
+      "dev": true,
+      "dependencies": {
+        "@exodus/bytes": "^1.11.0",
+        "tr46": "^6.0.0",
+        "webidl-conversions": "^8.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "dev": true,
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/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/wrap-ansi/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "node_modules/xml-name-validator": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/xmlchars": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+      "dev": true
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "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/yargs": {
+      "version": "18.0.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+      "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^9.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "string-width": "^7.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^22.0.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=23"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "22.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+      "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+      "dev": true,
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=23"
+      }
+    },
+    "node_modules/yargs/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/yoctocolors": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
+      "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/yoctocolors-cjs": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+      "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zod": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+      "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    },
+    "node_modules/zod-to-json-schema": {
+      "version": "3.25.2",
+      "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz",
+      "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==",
+      "dev": true,
+      "peerDependencies": {
+        "zod": "^3.25.28 || ^4"
+      }
+    }
+  }
+}

+ 33 - 0
final project/web-game-shop/package.json

@@ -0,0 +1,33 @@
+{
+  "name": "web-game-shop",
+  "version": "0.0.0",
+  "scripts": {
+    "ng": "ng",
+    "start": "ng serve",
+    "build": "ng build",
+    "watch": "ng build --watch --configuration development",
+    "test": "ng test"
+  },
+  "private": true,
+  "packageManager": "npm@9.9.4",
+  "dependencies": {
+    "@angular/common": "^21.2.0",
+    "@angular/compiler": "^21.2.0",
+    "@angular/core": "^21.2.0",
+    "@angular/forms": "^21.2.0",
+    "@angular/platform-browser": "^21.2.0",
+    "@angular/router": "^21.2.0",
+    "@stripe/stripe-js": "^9.8.0",
+    "rxjs": "~7.8.0",
+    "tslib": "^2.3.0"
+  },
+  "devDependencies": {
+    "@angular/build": "^21.2.11",
+    "@angular/cli": "^21.2.11",
+    "@angular/compiler-cli": "^21.2.0",
+    "jsdom": "^28.0.0",
+    "prettier": "^3.8.1",
+    "typescript": "~5.9.2",
+    "vitest": "^4.0.8"
+  }
+}

BIN
final project/web-game-shop/public/favicon.ico


+ 13 - 0
final project/web-game-shop/src/app/app.config.ts

@@ -0,0 +1,13 @@
+import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
+import { provideRouter, withComponentInputBinding } from '@angular/router';
+import { routes } from './app.routes';
+import {provideHttpClient, withInterceptors} from '@angular/common/http';
+import {authInterceptor} from './interfaces/auth';
+
+export const appConfig: ApplicationConfig = {
+  providers: [
+    provideBrowserGlobalErrorListeners(),
+    provideRouter(routes, withComponentInputBinding()),
+    provideHttpClient(withInterceptors([authInterceptor]))
+  ]
+};

+ 0 - 0
final project/web-game-shop/src/app/app.css


+ 8 - 0
final project/web-game-shop/src/app/app.html

@@ -0,0 +1,8 @@
+<div class="m-0 p-0 bg-nexus-bg text-nexus-text-primary font-sans antialiased min-h-screen flex flex-col">
+  <app-header></app-header>
+
+  <main class="flex-1 pt-20">
+    <router-outlet></router-outlet>
+  </main>
+  <app-footer></app-footer>
+</div>

+ 34 - 0
final project/web-game-shop/src/app/app.routes.ts

@@ -0,0 +1,34 @@
+import { Routes } from '@angular/router';
+import { StorefrontComponent } from './components/storefront/storefront';
+import { DeveloperComponent } from './components/developer/developer';
+import { GameCatalogComponent } from './components/game-catalog/game-catalog';
+import { RegisterComponent } from './components/register/register';
+import { LoginComponent } from './components/login/login';
+import { ProfileComponent } from './components/profile/profile';
+// ADDED: Import the game details component explicitly into primary route directory scope
+import { GameDetailsComponent } from './components/game-details/game-details';
+import {CartComponent} from './components/cart/cart';
+import {LibraryComponent} from './components/library/library';
+import {BugReportComponent} from './components/bug-report/bug-report';
+
+
+export const routes: Routes = [
+  // Redirect root path directly into the neon grid storefront page module context
+  { path: '', redirectTo: 'store', pathMatch: 'full' },
+
+  { path: 'store', component: StorefrontComponent },
+
+  //  Dynamic parameter path routing target blueprint for single game items
+  { path: 'games/:id', component: GameDetailsComponent },
+  { path: 'developer', component: DeveloperComponent },
+  { path: 'catalog', component: GameCatalogComponent },
+  { path: 'register', component: RegisterComponent },
+  { path: 'login', component: LoginComponent },
+  { path: 'profile', component: ProfileComponent },
+  {path: 'cart', component: CartComponent},
+  {path: 'library', component: LibraryComponent},
+  {path: 'bug-report', component: BugReportComponent},
+
+  // Fallback pattern to gracefully redirect any undefined broken paths back home
+  { path: '**', redirectTo: 'store' }
+];

+ 23 - 0
final project/web-game-shop/src/app/app.spec.ts

@@ -0,0 +1,23 @@
+import { TestBed } from '@angular/core/testing';
+import { App } from './app';
+
+describe('App', () => {
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [App],
+    }).compileComponents();
+  });
+
+  it('should create the app', () => {
+    const fixture = TestBed.createComponent(App);
+    const app = fixture.componentInstance;
+    expect(app).toBeTruthy();
+  });
+
+  it('should render title', async () => {
+    const fixture = TestBed.createComponent(App);
+    await fixture.whenStable();
+    const compiled = fixture.nativeElement as HTMLElement;
+    expect(compiled.querySelector('h1')?.textContent).toContain('Hello, web-game-shop');
+  });
+});

+ 15 - 0
final project/web-game-shop/src/app/app.ts

@@ -0,0 +1,15 @@
+import { Component, signal } from '@angular/core';
+import { RouterOutlet } from '@angular/router';
+import {Footer} from './components/footer/footer';
+import {HeaderComponent} from './components/header/header';
+
+@Component({
+  selector: 'app-root',
+  standalone: true,
+  imports: [RouterOutlet, Footer, HeaderComponent],
+  templateUrl: './app.html',
+  styleUrl: './app.css'
+})
+export class App {
+  protected readonly title = signal('web-game-shop');
+}

BIN
final project/web-game-shop/src/app/assets/avatar.jpg


BIN
final project/web-game-shop/src/app/assets/background-register-login.png


BIN
final project/web-game-shop/src/app/assets/cyberpunk-bg.png


BIN
final project/web-game-shop/src/app/assets/dark-angel.png


+ 130 - 0
final project/web-game-shop/src/app/components/bug-report/bug-report.css

@@ -0,0 +1,130 @@
+.bug-report-container {
+  max-width: 700px;
+  margin: 100px auto;
+  padding: 32px;
+  background-color: #181824;
+  border-radius: 12px;
+  border: 1px solid #2e2e48;
+  color: #ffffff;
+  font-family: 'Rajdhani', sans-serif;
+}
+
+.bug-report-container .content-header {
+  margin-bottom: 24px;
+}
+
+.bug-report-container .content-header h1 {
+  font-size: 28px;
+  font-weight: 700;
+  margin-bottom: 8px;
+  color: #ffffff;
+}
+
+.bug-report-container .content-header p {
+  font-size: 16px;
+  color: #a0a0b8;
+  margin: 0;
+  font-family: sans-serif;
+}
+
+.bug-report-container .alert-success {
+  background-color: rgba(40, 167, 69, 0.15);
+  border: 1px solid #28a745;
+  color: #51d270;
+  padding: 12px 16px;
+  border-radius: 6px;
+  margin-bottom: 20px;
+  font-size: 14px;
+  font-family: sans-serif;
+}
+
+.bug-report-container .bug-form {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.bug-report-container .bug-form .form-group {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.bug-report-container .bug-form .form-group label {
+  font-size: 14px;
+  color: #a0a0b8;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+}
+
+.bug-report-container .bug-form .form-group label span {
+  color: #ff4d4d;
+}
+
+.bug-report-container .bug-form .form-group input,
+.bug-report-container .bug-form .form-group textarea,
+.bug-report-container .bug-form .form-group select {
+  background-color: #0f0f15;
+  border: 1px solid #2e2e48;
+  border-radius: 6px;
+  padding: 12px 16px;
+  color: #ffffff;
+  font-size: 15px;
+  font-family: sans-serif;
+  transition: border-color 0.2s ease;
+}
+
+.bug-report-container .bug-form .form-group input:focus,
+.bug-report-container .bug-form .form-group textarea:focus,
+.bug-report-container .bug-form .form-group select:focus {
+  outline: none;
+  border-color: #5865f2;
+}
+
+.bug-report-container .bug-form .form-group input::placeholder,
+.bug-report-container .bug-form .form-group textarea::placeholder {
+  color: rgba(160, 160, 184, 0.5);
+}
+
+.bug-report-container .bug-form .form-group textarea {
+  resize: vertical;
+}
+
+.bug-report-container .bug-form .form-group .error-message {
+  color: #ff4d4d;
+  font-size: 13px;
+  font-family: sans-serif;
+}
+
+.bug-report-container .bug-form .form-row {
+  display: flex;
+  gap: 20px;
+}
+
+.bug-report-container .bug-form .form-row .col {
+  flex: 1;
+}
+
+.bug-report-container .bug-form .submit-btn {
+  background-color: #5865f2;
+  color: #ffffff;
+  border: none;
+  border-radius: 6px;
+  padding: 14px 24px;
+  font-size: 16px;
+  font-weight: 700;
+  cursor: pointer;
+  transition: background-color 0.2s ease, opacity 0.2s ease;
+  text-transform: uppercase;
+  letter-spacing: 1px;
+  margin-top: 10px;
+}
+
+.bug-report-container .bug-form .submit-btn:hover:not(:disabled) {
+  background-color: #4754d4;
+}
+
+.bug-report-container .bug-form .submit-btn:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}

+ 94 - 0
final project/web-game-shop/src/app/components/bug-report/bug-report.html

@@ -0,0 +1,94 @@
+<div class="bug-report-container">
+  <div class="content-header">
+    <h1>Bug Report</h1>
+    <p>Help us improve the platform by reporting any issues or unexpected behavior you encounter.</p>
+  </div>
+
+  <div *ngIf="isSuccess" class="alert-success">
+    Your bug report has been submitted successfully. Thank you!
+  </div>
+
+  <div *ngIf="errorMessage" class="alert-danger" style="color: #ff4d4d; margin-bottom: 20px;">
+    {{ errorMessage }}
+  </div>
+
+  <form [formGroup]="bugForm" (ngSubmit)="onSubmit()" class="bug-form">
+
+    <div class="form-group">
+      <label for="title">Issue Title <span>*</span></label>
+      <input
+        id="title"
+        type="text"
+        formControlName="title"
+        placeholder="Brief summary of the issue"
+      >
+      <div *ngIf="bugForm.get('title')?.invalid && bugForm.get('title')?.touched" class="error-message">
+        Title is required and must be at least 5 characters long.
+      </div>
+    </div>
+
+    <div class="form-group">
+      <label for="gameId">Game ID <span>*</span></label>
+      <input
+        id="gameId"
+        type="number"
+        formControlName="gameId"
+        readonly
+        style="opacity: 0.7; cursor: not-allowed;"
+      >
+      <div *ngIf="bugForm.get('gameId')?.invalid && bugForm.get('gameId')?.touched" class="error-message">
+        Valid Game ID is required. Go to Library and click 'Report Bug' on a game.
+      </div>
+    </div>
+
+    <div class="form-group">
+      <label for="description">Description <span>*</span></label>
+      <textarea
+        id="description"
+        formControlName="description"
+        rows="5"
+        placeholder="Describe the steps to reproduce the bug, expected vs actual results..."
+      ></textarea>
+      <div *ngIf="bugForm.get('description')?.invalid && bugForm.get('description')?.touched" class="error-message">
+        Description is required and must be at least 10 characters long.
+      </div>
+    </div>
+
+    <div class="form-row">
+      <div class="form-group col">
+        <label for="severity">Severity</label>
+        <select id="severity" formControlName="severity">
+          <option *ngFor="let sev of severityOptions" [value]="sev">{{ sev }}</option>
+        </select>
+      </div>
+
+      <div class="form-group col">
+        <label for="reproducible">Is it reproducible?</label>
+        <select id="reproducible" formControlName="reproducible">
+          <option value="yes">Yes</option>
+          <option value="no">No</option>
+          <option value="sometimes">Sometimes</option>
+        </select>
+      </div>
+    </div>
+
+    <div class="form-group">
+      <label for="contactEmail">Contact Email (Optional)</label>
+      <input
+        id="contactEmail"
+        type="email"
+        formControlName="contactEmail"
+        placeholder="To reach out for further details if needed"
+      >
+      <div *ngIf="bugForm.get('contactEmail')?.invalid && bugForm.get('contactEmail')?.touched" class="error-message">
+        Please enter a valid email address.
+      </div>
+    </div>
+
+    <button type="submit" [disabled]="bugForm.invalid || isSubmitting" class="submit-btn">
+      <span *ngIf="isSubmitting">Submitting...</span>
+      <span *ngIf="!isSubmitting">Submit Report</span>
+    </button>
+
+  </form>
+</div>

+ 22 - 0
final project/web-game-shop/src/app/components/bug-report/bug-report.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { BugReport } from './bug-report';
+
+describe('BugReport', () => {
+  let component: BugReport;
+  let fixture: ComponentFixture<BugReport>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [BugReport],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(BugReport);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 95 - 0
final project/web-game-shop/src/app/components/bug-report/bug-report.ts

@@ -0,0 +1,95 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { BugReportService } from '../../services/bug-report/bug-report';
+
+@Component({
+  selector: 'app-bug-report',
+  templateUrl: './bug-report.html',
+  imports: [
+    CommonModule,
+    ReactiveFormsModule
+  ],
+  styleUrls: ['./bug-report.css']
+})
+export class BugReportComponent implements OnInit {
+  bugForm!: FormGroup;
+  isSubmitting = false;
+  isSuccess = false;
+  errorMessage: string | null = null;
+
+  severityOptions = ['Low', 'Medium', 'High', 'Critical'];
+  currentUserId: number = 1;
+
+  constructor(
+    private fb: FormBuilder,
+    private bugReportService: BugReportService,
+    private router: Router
+  ) { }
+
+  ngOnInit(): void {
+    const session = JSON.parse(localStorage.getItem('nexus_mock_session') || '{}');
+    if (session && session.userId) {
+      this.currentUserId = session.userId;
+    }
+
+    let passedGameId = '';
+    const navigation = this.router.getCurrentNavigation();
+    const state = navigation?.extras.state as { gameId: number };
+
+    if (state && state.gameId) {
+      passedGameId = state.gameId.toString();
+    } else {
+      const storedId = localStorage.getItem('selected_game_id');
+      if (storedId) {
+        passedGameId = storedId;
+        localStorage.removeItem('selected_game_id');
+      }
+    }
+
+    this.bugForm = this.fb.group({
+      title: ['', [Validators.required, Validators.minLength(5)]],
+      description: ['', [Validators.required, Validators.minLength(10)]],
+      severity: ['Medium', Validators.required],
+      reproducible: ['yes', Validators.required],
+      contactEmail: ['', [Validators.email]],
+      gameId: [passedGameId, [Validators.required, Validators.min(1)]]
+    });
+  }
+
+  onSubmit(): void {
+    if (this.bugForm.valid) {
+      this.isSubmitting = true;
+      this.errorMessage = null;
+      this.isSuccess = false;
+
+      const formValue = this.bugForm.value;
+
+
+      const payload = {
+        title: formValue.title,
+        bodyText: formValue.description,
+        severityLevel: formValue.severity,
+        reproducible: formValue.reproducible,
+        contactEmail: formValue.contactEmail,
+        user: { userId: this.currentUserId },
+        game: { gameId: Number(formValue.gameId) }
+      };
+
+      this.bugReportService.submitBugReport(payload).subscribe({
+        next: (response) => {
+          this.isSubmitting = false;
+          this.isSuccess = true;
+          this.bugForm.reset({ severity: 'Medium', reproducible: 'yes' });
+          setTimeout(() => { this.isSuccess = false; }, 5000);
+        },
+        error: (err) => {
+          console.error('Failed to submit bug report', err);
+          this.errorMessage = 'Failed to submit bug report.';
+          this.isSubmitting = false;
+        }
+      });
+    }
+  }
+}

+ 215 - 0
final project/web-game-shop/src/app/components/cart/cart.css

@@ -0,0 +1,215 @@
+/* Cart Viewport Container */
+.cart-viewport-container {
+  max-width: 1200px;
+  margin-top: 40px;
+  padding: 40px 20px;
+  font-family: 'Rajdhani', sans-serif;
+  color: #ffffff;
+}
+
+.cart-header {
+  font-size: 2.5rem;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  margin-bottom: 40px;
+  background: linear-gradient(90deg, #00ffcc, #0099ff);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+  padding-bottom: 20px;
+}
+
+/* Empty Cart State */
+.empty-cart-state {
+  background: #121215;
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  border-radius: 8px;
+  padding: 60px 20px;
+  text-align: center;
+  margin-top: 40px;
+}
+
+.empty-cart-text {
+  color: #a0a0b8;
+  font-size: 1.25rem;
+  margin-bottom: 30px;
+}
+
+.shopping-cta-link {
+  display: inline-block;
+  background: rgba(0, 153, 255, 0.1);
+  border: 1px solid #0099ff;
+  color: #0099ff;
+  padding: 12px 32px;
+  text-decoration: none;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+}
+
+.shopping-cta-link:hover {
+  background: #0099ff;
+  color: #ffffff;
+  box-shadow: 0 0 15px rgba(0, 153, 255, 0.4);
+}
+
+/* Cart Content Grid */
+.cart-content-grid {
+  display: grid;
+  grid-template-columns: 2fr 1fr;
+  gap: 40px;
+  margin-top: 20px;
+}
+
+@media (max-width: 768px) {
+  .cart-content-grid {
+    grid-template-columns: 1fr;
+  }
+}
+
+.cart-items-list-column {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.cart-item-card {
+  background: #141419;
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  border-radius: 6px;
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  transition: border-color 0.2s ease;
+}
+
+.cart-item-card:hover {
+  border-color: rgba(0, 255, 204, 0.3);
+}
+
+.cart-item-details {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.cart-item-title {
+  font-size: 1.25rem;
+  font-weight: 700;
+  letter-spacing: 0.02em;
+  margin: 0;
+}
+
+.cart-item-price {
+  font-size: 1.1rem;
+  color: #00ffcc;
+  font-weight: 600;
+}
+
+.remove-item-cta {
+  background: rgba(255, 77, 77, 0.1);
+  border: 1px solid #ff4d4d;
+  color: #ff4d4d;
+  padding: 8px 16px;
+  font-family: 'Rajdhani', sans-serif;
+  font-weight: 700;
+  text-transform: uppercase;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.remove-item-cta:hover {
+  background: #ff4d4d;
+  color: #ffffff;
+  box-shadow: 0 0 10px rgba(255, 77, 77, 0.3);
+}
+
+/* Summary Column */
+.summary-card-box {
+  background: #141419;
+  border: 1px solid rgba(255, 255, 255, 0.08);
+  border-radius: 8px;
+  padding: 24px;
+  position: sticky;
+  top: 20px;
+}
+
+.summary-card-box h3 {
+  font-size: 1.5rem;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  margin-top: 0;
+  margin-bottom: 20px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+  padding-bottom: 12px;
+}
+
+.summary-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 1.25rem;
+  margin-bottom: 24px;
+}
+
+.total-amount-display {
+  font-size: 1.75rem;
+  font-weight: 700;
+  color: #00ffcc;
+}
+
+/* Action Checkout Button */
+.primary-checkout-cta {
+  width: 100%;
+  background: linear-gradient(90deg, #00ffcc, #00b3ff);
+  border: none;
+  color: #05050a;
+  padding: 16px;
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 1.25rem;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.primary-checkout-cta:hover:not([disabled]) {
+  transform: translateY(-2px);
+  box-shadow: 0 0 20px rgba(0, 255, 204, 0.4);
+}
+
+.primary-checkout-cta:disabled {
+  background: #33333b;
+  color: #8e8e9c;
+  cursor: not-allowed;
+  transform: none;
+}
+
+/* Alert Banners */
+.purchase-alert-banner {
+  padding: 12px 16px;
+  border-radius: 4px;
+  font-size: 1.1rem;
+  margin-bottom: 20px;
+  font-weight: 600;
+}
+
+.purchase-alert-banner.success-mod {
+  background: rgba(0, 255, 128, 0.1);
+  border: 1px solid #00ff80;
+  color: #00ff80;
+}
+
+.purchase-alert-banner.failure-mod {
+  background: rgba(255, 77, 77, 0.1);
+  border: 1px solid #ff4d4d;
+  color: #ff4d4d;
+}

+ 44 - 0
final project/web-game-shop/src/app/components/cart/cart.html

@@ -0,0 +1,44 @@
+<div class="cart-viewport-container">
+  <h2 class="cart-header">NEXUS Digital Shopping Cart Workspace</h2>
+
+  <div class="empty-cart-state" *ngIf="items.length === 0">
+    <p class="empty-cart-text">No digital license allocations currently pending inside the cart module.</p>
+    <a routerLink="/store" class="shopping-cta-link">Return to Storefront</a>
+  </div>
+
+  <div class="cart-content-grid" *ngIf="items.length > 0">
+    <div class="cart-items-list-column">
+      <div class="cart-item-card" *ngFor="let item of items">
+        <div class="cart-item-details">
+          <h4 class="cart-item-title">{{ item.title }}</h4>
+          <span class="cart-item-price">${{ calculateDiscountedPrice(item.price, item.discountPercent).toFixed(2) }} USD</span>
+        </div>
+        <button class="remove-item-cta" (click)="removeFromCart(item.gameId)">Remove Allocation</button>
+      </div>
+    </div>
+
+    <div class="cart-summary-column">
+      <div class="summary-card-box">
+        <h3>Order Summary Metrics</h3>
+        <div class="summary-row total-row">
+          <span>Total Payable Amount:</span>
+          <span class="total-amount-display">${{ getTotalPrice().toFixed(2) }} USD</span>
+        </div>
+
+        <div class="purchase-alert-banner success-mod" *ngIf="checkoutMessage">
+          <p>{{ checkoutMessage }}</p>
+        </div>
+        <div class="purchase-alert-banner failure-mod" *ngIf="errorMessage">
+          <p>{{ errorMessage }}</p>
+        </div>
+
+        <button
+          class="primary-checkout-cta"
+          [disabled]="isProcessingCheckout"
+          (click)="processCheckout()">
+          {{ isProcessingCheckout ? 'Processing Transaction Sequence...' : 'Execute Transaction & Checkout Allocation' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</div>

+ 22 - 0
final project/web-game-shop/src/app/components/cart/cart.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CartComponent } from './cart';
+
+describe('Cart', () => {
+  let component: CartComponent;
+  let fixture: ComponentFixture<CartComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [CartComponent],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(CartComponent);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 149 - 0
final project/web-game-shop/src/app/components/cart/cart.ts

@@ -0,0 +1,149 @@
+import { Component, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { Subscription } from 'rxjs';
+import { CartService } from '../../services/cart/cart';
+import { UserService } from '../../services/user/user';
+import { Game } from '../../models/game.model';
+
+@Component({
+  selector: 'app-cart',
+  standalone: true,
+  imports: [CommonModule, RouterModule],
+  templateUrl: './cart.html',
+  styleUrls: ['./cart.css']
+})
+export class CartComponent implements OnInit, OnDestroy {
+  items: Game[] = [];
+  isProcessingCheckout: boolean = false;
+  checkoutMessage: string | null = null;
+  errorMessage: string | null = null;
+
+  // Dynamic active user identifier token
+  currentUserId!: number;
+  private sessionSubscription!: Subscription;
+
+  constructor(
+    private cartService: CartService,
+    private userService: UserService, // Injected UserService
+    private cdr: ChangeDetectorRef
+  ) {}
+
+  ngOnInit(): void {
+    this.loadCart();
+
+    // Subscribing to live active session to extract real user ID
+    this.sessionSubscription = this.userService.currentSession$.subscribe({
+      next: (session) => {
+        if (session && session.userId) { // Assuming session object contains userId property
+          this.currentUserId = session.userId;
+        } else {
+          // Fallback or default ID if session is not established
+          this.currentUserId = 1;
+        }
+        this.cdr.detectChanges();
+      }
+    });
+  }
+
+  ngOnDestroy(): void {
+    if (this.sessionSubscription) {
+      this.sessionSubscription.unsubscribe();
+    }
+  }
+
+  loadCart(): void {
+    this.items = this.cartService.getCartItems();
+    // Update the header badge count whenever the cart items are loaded/refreshed
+    this.cartService.updateCartItemCount(this.items.length);
+  }
+
+
+  removeFromCart(gameId: number | undefined): void {
+    this.cartService.removeFromCart(gameId);
+    this.loadCart();
+    this.cdr.detectChanges();
+  }
+
+  calculateDiscountedPrice(price: number, discountPercent: number | undefined): number {
+    if (!discountPercent || discountPercent <= 0) return price;
+    return Number((price * (1 - discountPercent / 100)).toFixed(2));
+  }
+
+  getTotalPrice(): number {
+    return this.items.reduce((total, item) => {
+      const priceToUse = item.discountPercent ? this.calculateDiscountedPrice(item.price, item.discountPercent) : item.price;
+      return total + priceToUse;
+    }, 0);
+  }
+
+  /**
+   * Sequentially purchases all items currently inside the shopping cart.
+   */
+  processCheckout(): void {
+    if (this.items.length === 0 || this.isProcessingCheckout) return;
+
+    this.isProcessingCheckout = true;
+    this.checkoutMessage = null;
+    this.errorMessage = null;
+
+    // Safety check for dynamic user ID before dispatching HTTP calls
+    if (!this.currentUserId) {
+      this.errorMessage = 'TRANSACTION BLOCKED: Active user session could not be resolved.';
+      this.isProcessingCheckout = false;
+      return;
+    }
+
+    // Start sequential processing from the first index
+    this.purchaseItemsSequentially(0);
+  }
+
+  /**
+   * Helper method to process cart items one by one recursively.
+   */
+  private purchaseItemsSequentially(index: number): void {
+    // Base case: if all items are processed, clear the cart and finalize
+    if (index >= this.items.length) {
+      this.checkoutMessage = 'SUCCESS: All shopping cart allocations fully processed. Licenses assigned.';
+      this.cartService.clearCart();
+      this.loadCart();
+      this.isProcessingCheckout = false;
+      this.cdr.detectChanges();
+      return;
+    }
+
+    const currentItem = this.items[index];
+
+    this.cartService.checkoutGame(this.currentUserId, currentItem.gameId).subscribe({
+      next: (response) => {
+        // Proceed to the next item in the array
+        this.purchaseItemsSequentially(index + 1);
+      },
+      error: (err) => {
+        console.error('Checkout transaction faulted:', err);
+
+        // Student Note: Safely extract server message parameters to identify business rule exceptions
+        let errorStringPayload = '';
+        if (err.error) {
+          errorStringPayload = typeof err.error === 'string' ? err.error : JSON.stringify(err.error);
+        }
+
+        const fallbackMessage = err.message || '';
+
+        // Check for specific backend business constraint violations safely within our string parameters
+        if (errorStringPayload.includes('ALREADY_OWNED') || fallbackMessage.includes('ALREADY_OWNED')) {
+          // A completely friendly, non-technical notification for the user
+          this.errorMessage = `You already own "${currentItem.title}" in your library. You cannot purchase this game again.`;
+        } else if (errorStringPayload.includes('Insufficient funds') || errorStringPayload.includes('credit balance')) {
+          this.errorMessage = `Transaction Failed: Insufficient wallet balance to complete the purchase for "${currentItem.title}".`;
+        } else {
+          // General systemic issue fallback configuration
+          this.errorMessage = `Transaction Failed: Could not process the purchase for "${currentItem.title}" due to a system conflict.`;
+        }
+
+        this.isProcessingCheckout = false;
+        this.cdr.detectChanges();
+      }
+    });
+  }
+}

+ 0 - 0
final project/web-game-shop/src/app/components/developer/developer.css


+ 54 - 0
final project/web-game-shop/src/app/components/developer/developer.html

@@ -0,0 +1,54 @@
+<div class="management-container">
+
+  <div class="admin-form-box">
+    <h3>Developer Operations: Register Game Studio</h3>
+    <div class="form-group">
+      <input type="text" [(ngModel)]="newDeveloper.studioName" placeholder="Studio Name (mandatory)" />
+      <input type="email" [(ngModel)]="newDeveloper.contactEmail" placeholder="Corporate Contact Email (mandatory)" />
+      <input type="url" [(ngModel)]="newDeveloper.website" placeholder="Official Website URL (https://...)" />
+    </div>
+    <button (click)="addDeveloper()" class="add-btn">Persist Studio Profile</button>
+  </div>
+
+  <hr />
+
+  <h2>Contracted Game Development Studios</h2>
+
+  <div class="table-responsive">
+    <table class="dev-table">
+      <thead>
+      <tr>
+        <th>ID</th>
+        <th>Studio Name</th>
+        <th>Contact Channel</th>
+        <th>Web Domain</th>
+        <th>Published Titles Count</th>
+        <th>Control Actions</th>
+      </tr>
+      </thead>
+      <tbody>
+        @for (dev of developers; track dev.developerId) {
+          <tr>
+            <td><strong>#{{ dev.developerId }}</strong></td>
+            <td><span class="studio-highlight">{{ dev.studioName }}</span></td>
+            <td>{{ dev.contactEmail }}</td>
+            <td>
+              <a *ngIf="dev.website" [href]="dev.website" target="_blank" class="web-link">{{ dev.website }}</a>
+              <span *ngIf="!dev.website" class="muted-text">None</span>
+            </td>
+            <td>
+              <span class="badge count-badge">{{ dev.games?.length || 0 }} games</span>
+            </td>
+            <td>
+              <button (click)="deleteDeveloper(dev.developerId)" class="delete-btn-sm">Terminate Profile</button>
+            </td>
+          </tr>
+        } @empty {
+          <tr>
+            <td colspan="6" class="empty-row-msg">No hardware or software development entities registered yet.</td>
+          </tr>
+        }
+      </tbody>
+    </table>
+  </div>
+</div>

+ 23 - 0
final project/web-game-shop/src/app/components/developer/developer.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import {Developer} from '../../models/developer.model';
+
+
+describe('Developer', () => {
+  let component: Developer;
+  let fixture: ComponentFixture<Developer>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [fixture],
+    }).compileComponents();
+
+    // @ts-ignore
+    fixture = TestBed.createComponent(fixture);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 86 - 0
final project/web-game-shop/src/app/components/developer/developer.ts

@@ -0,0 +1,86 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { HttpClient } from '@angular/common/http';
+import { Developer } from '../../models/developer.model';
+
+@Component({
+  selector: 'app-developer-management',
+  standalone: true,
+  imports: [CommonModule, FormsModule],
+  templateUrl: './developer.html',
+  styleUrls: ['./developer.css'] // Standard component styling link
+})
+export class DeveloperComponent implements OnInit {
+  developers: Developer[] = [];
+
+  // Clean form state representation matching Java constraints
+  newDeveloper = {
+    studioName: '',
+    contactEmail: '',
+    website: ''
+  };
+
+  private apiUrl = 'http://localhost:8085/api/developers'; // Adjust destination endpoint if needed
+
+  constructor(
+    private http: HttpClient,
+    private cdr: ChangeDetectorRef
+  ) {}
+
+  ngOnInit(): void {
+    this.loadDevelopers();
+  }
+
+  loadDevelopers(): void {
+    this.http.get<any>(this.apiUrl).subscribe({
+      next: (data) => {
+        console.log('Raw developer payload fetched:', data);
+        // Normalize checking for Spring Data Page wrappers
+        this.developers = data.content ? data.content : (Array.isArray(data) ? data : [data]);
+
+        // Force immediate template state synchronisation
+        this.cdr.detectChanges();
+      },
+      error: (err) => console.error('Failed to resolve developer index data:', err)
+    });
+  }
+
+  addDeveloper(): void {
+    if (!this.newDeveloper.studioName || !this.newDeveloper.contactEmail) {
+      alert('Studio Name and Contact Email are mandatory fields!');
+      return;
+    }
+
+    this.http.post<Developer>(this.apiUrl, this.newDeveloper).subscribe({
+      next: (savedDev) => {
+        alert(`Studio "${savedDev.studioName}" registered successfully with ID: ${savedDev.developerId}`);
+        this.resetForm();
+        this.loadDevelopers(); // Auto refresh current view stack
+      },
+      error: (err) => {
+        console.error('Persistence validation rejected by Spring Boot:', err);
+        alert('Server rejection! Verify if emails are valid or check network logs.');
+      }
+    });
+  }
+
+  deleteDeveloper(id: number | undefined): void {
+    if (!id || !confirm('Are you sure? Deleting a studio will purge all associated game listings!')) return;
+
+    this.http.delete(`${this.apiUrl}/${id}`).subscribe({
+      next: () => {
+        alert('Developer studio profile entirely dropped from tables.');
+        this.loadDevelopers();
+      },
+      error: (err) => {
+        console.error('FK constraint error during dropping procedure:', err);
+        alert('Database integrity rejection: Cannot drop studios holding active software store contracts.');
+      }
+    });
+  }
+
+  resetForm(): void {
+    this.newDeveloper = { studioName: '', contactEmail: '', website: '' };
+  }
+}

+ 130 - 0
final project/web-game-shop/src/app/components/footer/footer.css

@@ -0,0 +1,130 @@
+/* Container layout rules for the global footer section aligned with the design system */
+.global-footer {
+  background-color: #0b0b12;
+  border-top: 1px solid rgba(255, 255, 255, 0.08);
+  padding: 64px 32px 32px 32px;
+  margin-top: 64px;
+}
+
+/* Master grid layout for aligning the navigation columns and brand block */
+.footer-structural-grid {
+  max-width: 1400px;
+  margin: 0 auto;
+  display: grid;
+  grid-template-columns: 2fr 1fr 1fr;
+  gap: 48px;
+  padding-bottom: 48px;
+}
+
+/* Left metadata column positioning definitions */
+.footer-brand-column {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+  max-width: 320px;
+}
+
+.footer-brand-anchor {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  text-decoration: none;
+  color: #ffffff;
+}
+
+.footer-brand-logo {
+  font-family: 'Rajdhani', sans-serif;
+  font-weight: 700;
+  font-size: 24px;
+  letter-spacing: 0.05em;
+}
+
+.footer-brand-description {
+  color: #94a3b8;
+  font-size: 14px;
+  line-height: 1.6;
+  margin: 0;
+}
+
+/* Central and right link mapping navigation blocks */
+.footer-nav-column {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.footer-column-heading {
+  color: #ffffff;
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 16px;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  margin: 0;
+}
+
+.footer-links-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.footer-interactive-link {
+  color: #64748b;
+  text-decoration: none;
+  font-size: 14px;
+  font-weight: 500;
+  transition: color 0.2s ease-in-out;
+}
+
+.footer-interactive-link:hover {
+  color: #7E5BD8;
+}
+
+/* Social icons layout grouping specifications */
+.footer-socials-wrapper {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.social-vector-trigger {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 36px;
+  height: 36px;
+  background-color: rgba(255, 255, 255, 0.03);
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  border-radius: 8px;
+  color: #94a3b8;
+  transition: all 0.2s ease-in-out;
+}
+
+.social-vector-trigger:hover {
+  color: #ffffff;
+  background-color: #7E5BD8;
+  border-color: #7E5BD8;
+}
+
+/* Bottom copyright row layout separation definitions */
+.footer-copyright-divider {
+  border-top: 1px solid rgba(255, 255, 255, 0.04);
+  max-width: 1400px;
+  margin: 0 auto;
+  padding-top: 24px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.copyright-legal-text {
+  color: #64748b;
+  font-size: 13px;
+  margin: 0;
+}
+
+.legal-links-capsule {
+  display: flex;
+  gap: 24px;
+}

+ 54 - 0
final project/web-game-shop/src/app/components/footer/footer.html

@@ -0,0 +1,54 @@
+<!-- Structural representation of the platform global footer node -->
+<footer class="global-footer">
+  <div class="footer-structural-grid">
+
+    <!-- Brand identity and mission metrics column -->
+    <div class="footer-brand-column">
+      <a href="#" class="footer-brand-anchor">
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14px" height="14px" fill="currentColor">
+          <path d="M389.2 48h70.6L305.6 224.2 487 464H345l-111.9-146.6L106.3 464H35.7L247.3 224.2 64 48h145.1l111 146.6L389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/>
+        </svg>
+        <span class="footer-brand-logo">NEXUS</span>
+      </a>
+      <p class="footer-brand-description">
+        The ultimate next-generation digital storefront for gamers and developers. Explore, track, and secure your legendary library.
+      </p>
+    </div>
+
+    <!-- Platform links column navigation nodes mapping -->
+    <div class="footer-nav-column">
+      <h3 class="footer-column-heading">Navigation</h3>
+      <div class="footer-links-list">
+        <a href="#" class="footer-interactive-link">Storefront</a>
+        <a href="#" class="footer-interactive-link">Game Details</a>
+        <a href="#" class="footer-interactive-link">Customer Dash</a>
+        <a href="#" class="footer-interactive-link">Library Tracking</a>
+      </div>
+    </div>
+
+    <!-- Social engagement links and external handles block -->
+    <div class="footer-nav-column">
+      <h3 class="footer-column-heading">Connect with Us</h3>
+      <div class="footer-socials-wrapper">
+        <!-- Discord Icon -->
+        <a href="#" class="social-vector-trigger" aria-label="Discord Channel">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16px" height="16px" fill="currentColor"><path d="M297.2 248.9c0 18.6-15.4 33.7-34.3 33.7-18.9 0-34.3-15.1-34.3-33.7s15.4-33.7 34.3-33.7c18.9 0 34.3 15.1 34.3 33.7zm114.1-119c-31.4-29.2-73.4-48-121.7-51.5l-8.6 19.4c38.6 10.3 74.4 28.6 104.7 52.9-26.6-14.9-56.2-25.2-87.7-30.2-21.1-3.4-42.8-4.5-64.2-3.4-32.6 1.7-64.2 8-93.8 19-12.1 4.5-23.7 10-34.9 16.5l-3.3 1.9c4 2.4 8 4.7 12.2 6.9 29.1 15.4 60.5 25.5 93.1 29.8l-7.4-16.7c-40.4 2.3-78.2 16.1-110.1 39.1 30.2-23.9 65.4-41.9 103.2-51.9l-8.6-19.4c-48.3 3.5-90.3 22.3-121.7 51.5-4.5 4.5-54.8 96.5-54.8 214.6 0 1.3 .4 2.5 1.1 3.5 31.8 45.9 85.8 72.8 141.4 72.8 2.3 0 4.2-1.7 4.5-3.9 12.2-16.7 22.6-34.7 31-53.7 .8-1.8 0-3.8-1.7-4.7-17.6-9.1-33.8-20.2-48.8-33.1-1.9-1.7-1.9-4.7 0-6.4l10-8.6c1.6-1.4 4-.1 4.2 2 31.4 26.6 71.3 42.6 114.3 42.6 43 0 82.9-16 114.3-42.6 .3-2.1 2.6-3.4 4.2-2l10 8.6c1.9 1.7 1.9 4.7 0 6.4-15 12.9-31.2 24-48.8 33.1-1.7 .9-2.5 2.9-1.7 4.7 8.4 19 18.8 37 31 53.7 .3 2.3 2.2 3.9 4.5 3.9 55.6 0 109.6-26.9 141.4-72.8 .7-1 1.1-2.2 1.1-3.5 0-118.1-50.3-210.1-54.8-214.6zm-225.2 152.7c-18.9 0-34.3-15.1-34.3-33.7s15.4-33.7 34.3-33.7c18.9 0 34.3 15.1 34.3 33.7s-15.4 33.7-34.3 33.7z"/></svg>
+        </a>
+        <!-- Twitter/X Icon -->
+        <a href="#" class="social-vector-trigger" aria-label="Twitter Account">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14px" height="14px" fill="currentColor"><path d="M389.2 48h70.6L305.6 224.2 487 464H345 Lori L312.1 340.6 185.5 464H114.9L277.3 278.1 106.3 48h145.1l111 146.6L389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>
+        </a>
+      </div>
+    </div>
+
+  </div>
+
+  <!-- Copyright metrics and legal references sequence -->
+  <div class="footer-copyright-divider">
+    <p class="copyright-legal-text">&copy; 2026 NEXUS Digital Ltd. All rights reserved.</p>
+    <div class="legal-links-capsule">
+      <a href="#" class="footer-interactive-link" style="font-size: 13px;">Terms of Service</a>
+      <a href="#" class="footer-interactive-link" style="font-size: 13px;">Privacy Policy</a>
+    </div>
+  </div>
+</footer>

+ 22 - 0
final project/web-game-shop/src/app/components/footer/footer.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { Footer } from './footer';
+
+describe('Footer', () => {
+  let component: Footer;
+  let fixture: ComponentFixture<Footer>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [Footer],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(Footer);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 9 - 0
final project/web-game-shop/src/app/components/footer/footer.ts

@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-footer',
+  imports: [],
+  templateUrl: './footer.html',
+  styleUrl: './footer.css',
+})
+export class Footer {}

+ 90 - 0
final project/web-game-shop/src/app/components/game-catalog/game-catalog.css

@@ -0,0 +1,90 @@
+/* Container workspace layouts */
+.catalog-container {
+  padding: 20px;
+  margin-top: 90px;
+  font-family: Arial, sans-serif;
+}
+
+/* Responsive grid setup for display boards */
+.game-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 20px;
+  margin-top: 90px;
+}
+
+/* Individual product dashboard item formatting */
+.game-card {
+  border: 1px solid #ccc;
+  border-radius: 8px;
+  padding: 16px;
+  background-color: #f9f9f9;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  box-shadow: 0 4px 6px rgba(0,0,0,0.05);
+}
+
+.game-card h3 {
+  margin-top: 0;
+  color: #333;
+}
+
+.game-description {
+  color: #666;
+  font-size: 14px;
+  min-height: 40px;
+}
+
+/* Tags and badge styles */
+.game-meta {
+  margin: 10px 0;
+}
+
+.badge {
+  display: inline-block;
+  background: #e2e8f0;
+  color: #4a5568;
+  font-size: 12px;
+  padding: 4px 8px;
+  border-radius: 4px;
+  margin-right: 5px;
+}
+
+/* Financial components rendering layout */
+.price-section {
+  margin: 15px 0;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.price-tag {
+  font-size: 18px;
+  color: #2d3748;
+}
+
+.discount-tag {
+  background-color: #e53e3e;
+  color: white;
+  padding: 2px 6px;
+  font-size: 12px;
+  font-weight: bold;
+  border-radius: 4px;
+}
+
+/* Operational action request control nodes */
+.buy-btn {
+  background-color: #3182ce;
+  color: white;
+  border: none;
+  padding: 10px;
+  border-radius: 4px;
+  cursor: pointer;
+  font-weight: bold;
+  transition: background-color 0.2s;
+}
+
+.buy-btn:hover {
+  background-color: #2b6cb0;
+}

+ 36 - 0
final project/web-game-shop/src/app/components/game-catalog/game-catalog.html

@@ -0,0 +1,36 @@
+<div class="game-grid">
+  @for (game of games; track game.gameId) {
+    <div class="game-card">
+
+      <div class="game-card-media-wrapper"
+           (click)="navigateToGameDetails(game)"
+           style="width: 100%; height: 160px; background-color: #222; overflow: hidden; border-radius: 8px; margin-bottom: 12px; cursor: pointer;">
+        @if (game.imagePath) {
+          <img [src]="backendServerUrl + game.imagePath" alt="Live cover art source" style="width: 100%; height: 100%; object-fit: cover;" />
+        } @else {
+          <div style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; color: #666; font-size: 13px;">No Artwork Loaded</div>
+        }
+      </div>
+
+      <h3 (click)="navigateToGameDetails(game)" style="cursor: pointer; transition: color 0.2s ease;" onmouseover="this.style.color='#00d2ff'" onmouseout="this.style.color='#ffffff'">
+        {{ game.title }}
+      </h3>
+      <p>{{ game.description }}</p>
+      <p>Price: ${{ game.price }}</p>
+
+      <div class="action-buttons" style="display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px;">
+        <button (click)="navigateToGameDetails(game)" class="add-btn" style="background: linear-gradient(90deg, #7E5BD8 0%, #00d2ff 100%); padding: 6px 12px; font-size: 13px;">
+          View Details
+        </button>
+        <button (click)="setupEditForm(game)" class="add-btn" style="background-color: #222235; border: 1px solid #7E5BD8; padding: 6px 12px; font-size: 13px; color: #7E5BD8;">
+          Edit
+        </button>
+        <button (click)="removeGame(game.gameId)" class="delete-btn" style="padding: 6px 12px; font-size: 13px;">
+          Delete
+        </button>
+      </div>
+    </div>
+  } @empty {
+    <p>No games found in the database.</p>
+  }
+</div>

+ 23 - 0
final project/web-game-shop/src/app/components/game-catalog/game-catalog.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+// @ts-ignore
+import { GameCatalog } from "";
+
+describe('GameCatalog', () => {
+  let component: GameCatalog;
+  let fixture: ComponentFixture<GameCatalog>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [GameCatalog],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(GameCatalog);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 173 - 0
final project/web-game-shop/src/app/components/game-catalog/game-catalog.ts

@@ -0,0 +1,173 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import {Router, RouterModule} from '@angular/router'; // ADDED: Import angular router module interface
+import { GameService } from '../../services/game/game';
+import { Game } from '../../models/game.model';
+
+@Component({
+  selector: 'app-game-catalog',
+  standalone: true,
+  imports: [CommonModule, FormsModule, RouterModule],
+  templateUrl: './game-catalog.html',
+  styleUrls: ['./game-catalog.css']
+})
+export class GameCatalogComponent implements OnInit {
+  games: Game[] = [];
+  currentUserId: number = 1;
+
+  selectedFile: File | null = null;
+  public backendServerUrl: string;
+
+  // Tracking mechanisms determining whether the form behaves as insert or update procedure
+  isEditMode: boolean = false;
+  editingGameId: number | null = null;
+
+  newGame = {
+    title: '',
+    description: '',
+    price: 0,
+    discountPercent: 0,
+    version: '1.0.0',
+    tagsList: '',
+    developer: { developerId: 1 }
+  };
+
+  constructor(
+    private gameService: GameService,
+    private cdr: ChangeDetectorRef,
+    private router: Router // ADDED: Injected router service subsystem handler
+  ) {
+    this.backendServerUrl = this.gameService.hostMediaRoot;
+  }
+
+  ngOnInit(): void {
+    this.loadCatalog();
+  }
+
+  loadCatalog(): void {
+    this.gameService.getGames().subscribe({
+      next: (data: any) => {
+        const rawArray = data.content ? data.content : (Array.isArray(data) ? data : [data]);
+        this.games = [...rawArray];
+        this.cdr.detectChanges();
+      },
+      error: (err) => console.error('Failed to pull software records from server:', err)
+    });
+  }
+
+  navigateToGameDetails(game: Game): void {
+    console.log('Selected game object:', game); // Temporarily intercept telemetry logic to inspect incoming properties
+
+    // Fallback safely to check if the property maps to gameId or fallback plain id
+    const targetId = game.gameId || (game as any).id;
+
+    if (!targetId) {
+      console.warn('Routing aborted: Missing valid target identifier code mapping.');
+      return;
+    }
+
+    // Write historical telemetry pointer to track last viewed product item
+    localStorage.setItem('nexus_last_viewed_game_id', targetId.toString());
+
+    // Route browser viewport session layer onto the target game details screen gateway
+    this.router.navigate(['/games', targetId]);
+  }
+
+  onFileSelected(event: any): void {
+    const file: File = event.target.files[0];
+    if (file) {
+      this.selectedFile = file;
+    }
+  }
+
+  // Pre-populates management dashboard controllers using selected entities metadata parameters
+  setupEditForm(game: Game): void {
+    if (!game.gameId) return;
+
+    this.isEditMode = true;
+    this.editingGameId = game.gameId;
+
+    this.newGame = {
+      title: game.title,
+      description: game.description,
+      price: game.price,
+      discountPercent: game.discountPercent || 0,
+      version: game.version || '1.0.0',
+      tagsList: game.tagsList || '',
+      developer: { developerId: game.developer?.developerId || 1 }
+    };
+
+    // Smoothly scroll down to administration card dashboard module view bounds
+    window.scrollTo({ top: 0, behavior: 'smooth' });
+  }
+
+  // Combined tracking master logic router parsing state directives
+  saveGameDataStructure(): void {
+    if (!this.newGame.title || this.newGame.price < 0) {
+      alert('Please fill out mandatory parameters correctly.');
+      return;
+    }
+
+    const gamePayload = {
+      title: this.newGame.title,
+      description: this.newGame.description,
+      price: Number(this.newGame.price),
+      discountPercent: Number(this.newGame.discountPercent),
+      version: this.newGame.version || '1.0.0',
+      tagsList: this.newGame.tagsList,
+      developer: { developerId: Number(this.newGame.developer.developerId) }
+    };
+
+    const formData = new FormData();
+    formData.append('game', JSON.stringify(gamePayload));
+
+    if (this.selectedFile) {
+      formData.append('image', this.selectedFile, this.selectedFile.name);
+    }
+
+    if (this.isEditMode && this.editingGameId !== null) {
+      // Execute PUT alteration route path
+      this.gameService.updateGame(this.editingGameId, formData).subscribe({
+        next: () => {
+          alert('Product details altered inside persistent tables successfully!');
+          this.resetForm();
+          this.loadCatalog();
+        },
+        error: (err) => console.error('Modification pipeline fault:', err)
+      });
+    } else {
+      // Execute POST injection route path
+      this.gameService.createGame(formData).subscribe({
+        next: () => {
+          alert('Product stored inside database successfully!');
+          this.resetForm();
+          this.loadCatalog();
+        },
+        error: (err) => console.error('Persistence request rejected:', err)
+      });
+    }
+  }
+
+  removeGame(gameId: number | undefined): void {
+    if (!gameId || !confirm('Are you sure you want to delete this listing from database?')) return;
+
+    this.gameService.deleteGame(gameId).subscribe({
+      next: () => {
+        alert('Record completely cleared from table structures.');
+        this.loadCatalog();
+      },
+      error: (err) => console.error('Foreign key violation or execution fault:', err)
+    });
+  }
+
+  resetForm(): void {
+    this.newGame = { title: '', description: '', price: 0, discountPercent: 0, version: '1.0.0', tagsList: '', developer: { developerId: 1 } };
+    this.selectedFile = null;
+    this.isEditMode = false;
+    this.editingGameId = null;
+
+    const fileInput = document.getElementById('imageFileInput') as HTMLInputElement;
+    if (fileInput) fileInput.value = '';
+  }
+}

+ 332 - 0
final project/web-game-shop/src/app/components/game-details/game-details.css

@@ -0,0 +1,332 @@
+.details-viewport-container {
+  max-width: 1400px;
+  margin: 0 auto;
+  padding: 40px 32px;
+  min-height: 80vh;
+  font-family: 'Rajdhani', 'Inter', sans-serif;
+  color: #ffffff;
+}
+
+.navigation-back-link-row {
+  margin-bottom: 24px;
+}
+
+.back-link-anchor {
+  background: none;
+  border: none;
+  color: #94a3b8;
+  font-size: 15px;
+  font-weight: 600;
+  cursor: pointer;
+  transition: color 0.2s ease;
+  padding: 0;
+}
+
+.back-link-anchor:hover {
+  color: #7E5BD8;
+}
+
+.details-structural-grid {
+  display: grid;
+  grid-template-columns: 1fr 1.2fr;
+  gap: 48px;
+  align-items: start;
+}
+
+@media (max-width: 968px) {
+  .details-structural-grid {
+    grid-template-columns: 1fr;
+    gap: 32px;
+  }
+}
+
+.master-cover-frame-wrapper {
+  position: relative;
+  width: 100%;
+  border-radius: 16px;
+  overflow: hidden;
+  background-color: #161623;
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  box-shadow: 0 20px 40px rgba(0,0,0,0.4);
+}
+
+.master-live-image-canvas {
+  width: 100%;
+  height: auto;
+  max-height: 540px;
+  object-fit: cover;
+  display: block;
+}
+
+.fallback-graphic-placeholder {
+  height: 400px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: linear-gradient(135deg, #1a1a2e 0%, #0b0b12 100%);
+  color: #475569;
+  font-size: 14px;
+  font-weight: 700;
+}
+
+.promo-discount-badge {
+  position: absolute;
+  top: 20px;
+  left: 20px;
+  background-color: #ef4444;
+  color: #ffffff;
+  font-weight: 800;
+  font-size: 14px;
+  padding: 6px 14px;
+  border-radius: 8px;
+  box-shadow: 0 8px 16px rgba(239, 68, 68, 0.4);
+}
+
+.main-product-title {
+  font-size: 42px;
+  font-weight: 800;
+  margin: 0 0 10px 0;
+  line-height: 1.1;
+}
+
+.developer-studio-attribution-row {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #64748b;
+  font-size: 14px;
+  font-weight: 600;
+}
+
+.attribution-value-badge {
+  background-color: rgba(126, 91, 216, 0.15);
+  color: #c0aeff;
+  padding: 2px 8px;
+  border-radius: 4px;
+  border: 1px solid rgba(126, 91, 216, 0.3);
+}
+
+.technical-specification-row {
+  display: flex;
+  gap: 16px;
+  margin: 28px 0;
+  border-top: 1px solid rgba(255, 255, 255, 0.05);
+  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+  padding: 16px 0;
+}
+
+.metric-capsule {
+  background-color: #161623;
+  border: 1px solid rgba(255, 255, 255, 0.03);
+  padding: 12px 20px;
+  border-radius: 10px;
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+  flex-grow: 1;
+}
+
+.metric-label {
+  font-size: 11px;
+  font-weight: 700;
+  color: #475569;
+  letter-spacing: 0.1em;
+}
+
+.metric-value {
+  font-size: 16px;
+  font-weight: 700;
+  color: #ffffff;
+}
+
+.block-subsection-header {
+  font-size: 15px;
+  font-weight: 700;
+  text-transform: uppercase;
+  color: #94a3b8;
+  margin: 0 0 12px 0;
+  border-left: 3px solid #7E5BD8;
+  padding-left: 10px;
+}
+
+.descriptive-narrative-block {
+  margin-bottom: 32px;
+}
+
+.text-content-paragraph {
+  color: #94a3b8;
+  font-size: 15px;
+  line-height: 1.6;
+  margin: 0;
+  white-space: pre-wrap;
+}
+
+.tags-flex-container {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.ui-data-tag-node {
+  background-color: #161623;
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  color: #c0aeff;
+  padding: 6px 14px;
+  border-radius: 6px;
+  font-size: 13px;
+  font-weight: 600;
+}
+
+.checkout-transaction-control-box {
+  background: linear-gradient(145deg, #161623 0%, #11111c 100%);
+  border: 1px solid rgba(126, 91, 216, 0.15);
+  border-radius: 16px;
+  padding: 24px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  gap: 24px;
+  box-shadow: 0 12px 24px rgba(0,0,0,0.2);
+}
+
+.historical-slashed-price-display {
+  font-size: 13px;
+  color: #475569;
+  text-decoration: line-through;
+  margin-bottom: 2px;
+}
+
+.live-price-presentation-row {
+  display: flex;
+  align-items: baseline;
+  gap: 6px;
+}
+
+.price-currency-indicator {
+  font-size: 13px;
+  font-weight: 700;
+  color: #64748b;
+}
+
+.price-numeric-value-display {
+  font-size: 32px;
+  font-weight: 800;
+  color: #10b981;
+}
+
+.action-triggers-button-group {
+  flex-grow: 1;
+  max-width: 320px;
+}
+
+.primary-checkout-purchase-cta {
+  width: 100%;
+  background: linear-gradient(90deg, #7E5BD8 0%, #00d2ff 100%);
+  color: #ffffff;
+  border: none;
+  font-size: 15px;
+  font-weight: 700;
+  padding: 14px 24px;
+  border-radius: 10px;
+  cursor: pointer;
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+  box-shadow: 0 4px 14px rgba(126, 91, 216, 0.3);
+}
+
+.primary-checkout-purchase-cta:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 6px 20px rgba(0, 210, 255, 0.4);
+}
+
+.state-feedback-shell {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 16px;
+  padding: 80px 40px;
+  text-align: center;
+  color: #64748b;
+  font-size: 16px;
+}
+
+.loading-spinner-token {
+  width: 40px;
+  height: 40px;
+  border: 3px solid rgba(126, 91, 216, 0.1);
+  border-radius: 50%;
+  border-top-color: #7E5BD8;
+  animation: rotationFallbackSpinner 0.8s linear infinite;
+}
+
+@keyframes rotationFallbackSpinner {
+  to { transform: rotate(360deg); }
+}
+
+.warning-icon-badge {
+  font-size: 48px;
+  color: #ef4444;
+}
+
+.return-fallback-cta {
+  background-color: #161623;
+  color: #ffffff;
+  border: 1px solid rgba(255,255,255,0.1);
+  padding: 10px 20px;
+  border-radius: 8px;
+  cursor: pointer;
+}
+/* Purchase Transaction Notification Layout Banners */
+.purchase-alert-banner {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  padding: 16px 24px;
+  border-radius: 12px;
+  margin-bottom: 32px;
+  font-size: 15px;
+  font-weight: 600;
+  animation: slideInHUDAlert 0.3s cubic-bezier(0.16, 1, 0.3, 1);
+}
+
+.purchase-alert-banner.success-mod {
+  background-color: rgba(16, 185, 129, 0.1);
+  border: 1px solid rgba(16, 185, 129, 0.3);
+  color: #10b981;
+}
+
+.purchase-alert-banner.failure-mod {
+  background-color: rgba(239, 68, 68, 0.1);
+  border: 1px solid rgba(239, 68, 68, 0.3);
+  color: #ef4444;
+}
+
+.alert-icon {
+  font-size: 18px;
+  font-weight: 800;
+}
+
+.purchase-alert-banner p {
+  margin: 0;
+}
+
+@keyframes slideInHUDAlert {
+  from {
+    transform: translateY(-10px);
+    opacity: 0;
+  }
+  to {
+    transform: translateY(0);
+    opacity: 1;
+  }
+}
+
+/* Disabled button modification context styling properties */
+.primary-checkout-purchase-cta:disabled {
+  background: #27273a !important;
+  color: #475569 !important;
+  cursor: not-allowed;
+  box-shadow: none !important;
+  transform: none !important;
+}
+

+ 113 - 0
final project/web-game-shop/src/app/components/game-details/game-details.html

@@ -0,0 +1,113 @@
+<div class="details-viewport-container">
+
+  <div class="state-feedback-shell" *ngIf="isLoading">
+    <div class="loading-spinner-token"></div>
+    <p>Synchronizing with NEXUS central memory database tables...</p>
+  </div>
+
+  <div class="state-feedback-shell error-state-mod" *ngIf="!isLoading && errorMessage">
+    <span class="warning-icon-badge">⚠</span>
+    <p>{{ errorMessage }}</p>
+    <button class="return-fallback-cta" (click)="goBackToStorefront()">Return to Storefront Catalog</button>
+  </div>
+
+  <div class="game-details-layout" *ngIf="!isLoading && game">
+
+    <div class="navigation-back-link-row">
+      <button class="back-link-anchor" (click)="goBackToStorefront()">
+        ← Back to Storefront Catalog
+      </button>
+    </div>
+
+    <div class="purchase-alert-banner success-mod" *ngIf="purchaseSuccessMessage">
+      <span class="alert-icon">✓</span>
+      <p>{{ purchaseSuccessMessage }}</p>
+    </div>
+
+    <div class="purchase-alert-banner failure-mod" *ngIf="purchaseErrorMessage">
+      <span class="alert-icon">✕</span>
+      <p>{{ purchaseErrorMessage }}</p>
+    </div>
+
+    <div class="details-structural-grid">
+
+      <div class="media-presentation-column">
+        <div class="master-cover-frame-wrapper">
+          <ng-container *ngIf="game.imagePath; else fallbackDetailsCover">
+            <img [src]="backendServerUrl + game.imagePath" alt="{{ game.title }} resolution display canvas" class="master-live-image-canvas" />
+          </ng-container>
+          <ng-template #fallbackDetailsCover>
+            <div class="fallback-graphic-placeholder">
+              <span>NO PERSISTENT GRAPHIC MATERIAL PRE-LOADED</span>
+            </div>
+          </ng-template>
+
+          <div class="promo-discount-badge" *ngIf="game.discountPercent && game.discountPercent > 0">
+            -{{ game.discountPercent }}% OFF ACTIVE
+          </div>
+        </div>
+      </div>
+
+      <div class="metadata-specification-column">
+
+        <div class="title-identity-block">
+          <h1 class="main-product-title">{{ game.title }}</h1>
+
+          <div class="developer-studio-attribution-row" *ngIf="game.developer">
+            <span class="attribution-label">Studio Owner Developer ID:</span>
+            <span class="attribution-value-badge">#{{ game.developer.developerId }}</span>
+          </div>
+        </div>
+
+        <div class="technical-specification-row">
+          <div class="metric-capsule">
+            <span class="metric-label">BUILD VERSION</span>
+            <span class="metric-value">v{{ game.version || '1.0.0' }}</span>
+          </div>
+          <div class="metric-capsule">
+            <span class="metric-label">PERSISTENT IDENTITY INDEX</span>
+            <span class="metric-value">DB-REF #{{ game.gameId }}</span>
+          </div>
+        </div>
+
+        <div class="descriptive-narrative-block">
+          <h3 class="block-subsection-header">Product Overview & Store Page Records</h3>
+          <p class="text-content-paragraph">{{ game.description }}</p>
+        </div>
+
+        <div class="taxonomy-tags-block" *ngIf="game.tagsList">
+          <h3 class="block-subsection-header">Classified Taxonomy Metadata Tags</h3>
+          <div class="tags-flex-container">
+            <span class="ui-data-tag-node" *ngFor="let uniqueTag of game.tagsList.split(',')">
+              {{ uniqueTag.trim() }}
+            </span>
+          </div>
+        </div>
+
+        <div class="checkout-transaction-control-box">
+          <div class="pricing-evaluation-stack">
+            <span class="historical-slashed-price-display" *ngIf="game.discountPercent && game.discountPercent > 0">
+              Retail Valuation Original: ${{ game.price }}
+            </span>
+            <div class="live-price-presentation-row">
+              <span class="price-currency-indicator">USD</span>
+              <span class="price-numeric-value-display">
+                ${{ calculateDiscountedPrice(game.price, game.discountPercent) }}
+              </span>
+            </div>
+          </div>
+
+          <div class="action-triggers-button-group">
+              <button
+                class="primary-checkout-purchase-cta"
+                (click)="executeAddToCart()">
+                Add to Shopping Cart
+              </button>
+          </div>
+        </div>
+
+
+      </div>
+    </div>
+  </div>
+</div>

+ 22 - 0
final project/web-game-shop/src/app/components/game-details/game-details.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GameDetails } from './game-details';
+
+describe('GameDetails', () => {
+  let component: GameDetails;
+  let fixture: ComponentFixture<GameDetails>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [GameDetails],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(GameDetails);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 132 - 0
final project/web-game-shop/src/app/components/game-details/game-details.ts

@@ -0,0 +1,132 @@
+import { Component, OnInit, ChangeDetectorRef, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ActivatedRoute, RouterModule, Router } from '@angular/router';
+import { GameService } from '../../services/game/game';
+import { LibraryService } from '../../services/library/library'; // Added
+import { Game } from '../../models/game.model';
+import {CartService} from '../../services/cart/cart';
+
+@Component({
+  selector: 'app-game-details',
+  standalone: true,
+  imports: [CommonModule, RouterModule],
+  templateUrl: './game-details.html',
+  styleUrls: ['./game-details.css']
+})
+export class GameDetailsComponent implements OnInit {
+  game: Game | null = null;
+  isLoading: boolean = true;
+  errorMessage: string | null = null;
+
+  // Transaction alert state monitoring tokens
+  purchaseSuccessMessage: string | null = null;
+  purchaseErrorMessage: string | null = null;
+  isProcessingPurchase: boolean = false;
+
+  // Mocked active user context placeholder (Align with your session management system later)
+  private currentUserId: number = 1;
+
+  public backendServerUrl: string;
+
+  constructor(
+    private route: ActivatedRoute,
+    private router: Router,
+    private gameService: GameService,
+    private libraryService: LibraryService,
+    private cartService: CartService, // Added CartService
+    private cdr: ChangeDetectorRef
+  ) {
+    this.backendServerUrl = this.gameService.hostMediaRoot;
+  }
+
+  ngOnInit(): void {
+    this.route.paramMap.subscribe(params => {
+      const idParam = params.get('id');
+      if (idParam) {
+        const gameId = Number(idParam);
+        this.loadGameDetailsMetrics(gameId);
+      }
+    });
+  }
+
+  loadGameDetailsMetrics(gameId: number): void {
+    this.isLoading = true;
+    this.errorMessage = null;
+    this.gameService.getGameById(gameId).subscribe({
+      next: (gameRecord: Game) => {
+        if (gameRecord) {
+          this.game = gameRecord;
+        } else {
+          this.errorMessage = 'The requested software index record could not be localized.';
+        }
+        this.isLoading = false;
+        this.cdr.detectChanges();
+      },
+      error: (err) => {
+        console.error('REST endpoint connection sequence faulted:', err);
+        this.errorMessage = 'Failed to extract product metadata arrays from database persistent layers.';
+        this.isLoading = false;
+        this.cdr.detectChanges();
+      }
+    });
+  }
+
+  executeAddToCart(): void {
+    if (!this.game) return;
+
+    this.cartService.addToCart(this.game);
+    alert(`SUCCESS: "${this.game.title}" has been successfully appended to shopping cart indices.`);
+    // Optionally route directly to cart or show UI indicator
+    this.router.navigate(['/cart']);
+  }
+
+  /**
+   * Evaluates pricing states and triggers the backend wallet deduction/allocation protocol sequence.
+   */
+  executeLicenseAcquisition(): void {
+    console.log('Acquire button clicked. Evaluating state guards...');
+    console.log('Game object state:', this.game);
+    console.log('isProcessingPurchase flag:', this.isProcessingPurchase);
+
+    if (!this.game) {
+      console.warn('Action aborted: Game object is null or undefined.');
+      return;
+    }
+
+    if (this.isProcessingPurchase) {
+      console.warn('Action aborted: Purchase is already in progress.');
+      return;
+    }
+
+    this.isProcessingPurchase = true;
+    this.purchaseSuccessMessage = null;
+    this.purchaseErrorMessage = null;
+
+    const targetGameId = this.game.gameId;
+    console.log(`Dispatching purchase request for User ID: ${this.currentUserId} and Game ID: ${targetGameId}`);
+
+    this.libraryService.purchaseAndAddGame(this.currentUserId, targetGameId).subscribe({
+      next: (response) => {
+        console.log('Purchase transaction executed successfully:', response);
+        this.purchaseSuccessMessage = `SUCCESS: Allocation complete! "${this.game?.title}" has been permanently bound to your NEXUS Core Library.`;
+        this.isProcessingPurchase = false;
+        this.cdr.detectChanges();
+      },
+      error: (err) => {
+        console.error('Purchase sequence transaction faulted:', err);
+        this.purchaseErrorMessage = 'TRANSACTION DECLINED: Insufficient balance pool allocation. Please recharge your NEXUS Wallet metrics.';
+        this.isProcessingPurchase = false;
+        this.cdr.detectChanges();
+      }
+    });
+  }
+
+  calculateDiscountedPrice(price: number, discountPercent: number | undefined): number {
+    if (!discountPercent || discountPercent <= 0) return price;
+    return Number((price * (1 - discountPercent / 100)).toFixed(2));
+  }
+
+  goBackToStorefront(): void {
+    this.router.navigate(['/store']);
+  }
+}

+ 423 - 0
final project/web-game-shop/src/app/components/header/header.css

@@ -0,0 +1,423 @@
+/* Precise global mapping layouts mirroring Webflow element compilation structures */
+.header-global-navigation {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 72px;
+  z-index: 50;
+  background: rgba(26, 26, 46, 0.7);
+  backdrop-filter: blur(14px);
+  -webkit-backdrop-filter: blur(14px);
+  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
+  box-sizing: border-box;
+}
+
+.header-max-width-container {
+  width: 100%;
+  max-width: 1400px;
+  height: 100%;
+  margin: 0 auto;
+  padding: 0 40px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+}
+
+/* LEFT BLOCK CONFIGURATIONS: Brand branding token and capsulated link navigation tracks */
+.header-left-block {
+  display: flex;
+  align-items: center;
+  gap: 32px;
+  flex-shrink: 0;
+}
+
+.brand-identity-anchor {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  color: inherit;
+  text-decoration: none;
+}
+
+.brand-gradient-logo-box {
+  width: 36px;
+  height: 36px;
+  border-radius: 0.5rem;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 0 20px rgba(126, 91, 216, 0.35);
+  background-image: linear-gradient(to bottom right, #7E5BD8, #00d2ff);
+  color: #ffffff;
+}
+
+.logo-svg-vector {
+  width: 28px;
+  height: 28px;
+}
+
+.brand-name-text {
+  font-family: 'Rajdhani', sans-serif;
+  font-weight: 700;
+  font-size: 1.5rem;
+  letter-spacing: 0.1em;
+  color: #ffffff;
+  text-shadow: 0 0 12px rgba(126, 91, 216, 0.6);
+}
+
+.navigation-capsule-links {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.nav-link-item {
+  display: flex;
+  align-items: center;
+  padding: 8px 16px;
+  border-radius: 9999px;
+  color: #a0a0b8;
+  font-weight: 500;
+  font-size: 0.875rem;
+  text-decoration: none;
+  border: 1px solid transparent;
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.nav-link-item:hover, .nav-link-item.active-route {
+  color: #ffffff;
+  background-color: #1a1a2e;
+  border-color: #2a2a40;
+}
+
+/* CENTER BLOCK CONFIGURATIONS: Global catalog search capsule field element mapping */
+.header-center-search-block {
+  flex: 1;
+  max-width: 42rem;
+  margin: 0 auto;
+  position: relative;
+}
+
+.search-field-capsule {
+  position: relative;
+  width: 100%;
+  display: flex;
+  align-items: center;
+}
+
+.search-magnifier-icon-box {
+  position: absolute;
+  left: 16px;
+  top: 50%;
+  transform: translateY(-50%);
+  color: #a0a0b8;
+  display: flex;
+  align-items: center;
+}
+
+.search-svg {
+  width: 19px;
+  height: 19px;
+}
+
+.search-native-input {
+  width: 100%;
+  background-color: #0f0f16;
+  border: 1px solid #2a2a40;
+  border-radius: 9999px;
+  padding: 10px 48px 10px 44px;
+  font-size: 0.875rem;
+  color: #ffffff;
+  outline: none;
+  box-sizing: border-box;
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.search-native-input:focus {
+  border-color: #7E5BD8;
+  box-shadow: 0 0 0 1px #7E5BD8;
+}
+
+.search-hotkey-badge {
+  display: flex;
+  position: absolute;
+  right: 16px;
+  top: 50%;
+  transform: translateY(-50%);
+  align-items: center;
+  font-size: 10px;
+  color: #a0a0b8;
+  background-color: #2a2a40;
+  padding: 2px 8px;
+  border-radius: 0.25rem;
+  border: none;
+}
+
+/* RIGHT BLOCK CONFIGURATIONS: Guest triggers vs user transaction matrix views */
+.header-right-auth-block {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  flex-shrink: 0;
+}
+
+.guest-actions-group {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.link-sign-in-btn {
+  display: block;
+  padding: 10px 20px;
+  border-radius: 0.5rem;
+  border: 1px solid #2a2a40;
+  color: #a0a0b8;
+  font-size: 0.875rem;
+  font-weight: 600;
+  text-decoration: none;
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.link-sign-in-btn:hover {
+  color: #ffffff;
+  border-color: #7E5BD8;
+}
+
+.link-join-nexus-btn {
+  display: block;
+  padding: 10px 20px;
+  border-radius: 0.5rem;
+  background-color: #00ff88;
+  color: #0f0f16;
+  font-weight: 700;
+  font-size: 0.875rem;
+  text-decoration: none;
+  box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
+  white-space: nowrap;
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.link-join-nexus-btn:hover {
+  background-color: #34d399;
+}
+
+/* AUTHORIZED PROFILE STYLES ELEMENT MAPPINGS */
+.authorized-profile-badge-row {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+}
+
+.shopping-cart-wrapper {
+  position: relative;
+  cursor: pointer;
+  color: #a0a0b8;
+  transition: color 150ms ease;
+  display: flex;
+  align-items: center;
+}
+
+.shopping-cart-wrapper:hover {
+  color: #ffffff;
+}
+
+.cart-svg {
+  width: 20px;
+  height: 20px;
+}
+
+.cart-items-counter-badge {
+  position: absolute;
+  top: -6px;
+  right: -8px;
+  background-color: #7E5BD8;
+  color: #ffffff;
+  font-size: 10px;
+  font-weight: 700;
+  padding: 2px 5px;
+  border-radius: 9999px;
+  box-shadow: 0 0 10px rgba(126, 91, 216, 0.4);
+}
+
+.user-meta-profile-box {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  cursor: pointer;
+  border-left: 1px solid rgba(255, 255, 255, 0.08);
+  padding-left: 20px;
+}
+
+.text-metadata-stack {
+  text-align: right;
+}
+
+.user-profile-fullname {
+  font-size: 13px;
+  font-weight: 600;
+  color: #ffffff;
+  margin: 0;
+  line-height: 1.2;
+}
+
+.user-profile-role-status {
+  font-size: 11px;
+  color: #a0a0b8;
+  margin: 0;
+  line-height: 1.2;
+}
+
+.profile-avatar-img {
+  width: 36px;
+  height: 36px;
+  border-radius: 50%;
+  border: 1px solid rgba(126, 91, 216, 0.4);
+  object-fit: cover;
+}
+
+.chevron-down-vector {
+  width: 12px;
+  height: 12px;
+  color: #a0a0b8;
+}
+
+/* DEMO STATE TOGGLE UTILITY CONTROL ELEMENT */
+.demo-state-toggle-btn {
+  background: none;
+  border: 1px dashed rgba(255, 255, 255, 0.15);
+  color: #ffffff;
+  padding: 4px 8px;
+  border-radius: 6px;
+  cursor: pointer;
+  font-size: 12px;
+  opacity: 0.4;
+  transition: opacity 150ms ease;
+}
+
+.demo-state-toggle-btn:hover {
+  opacity: 1;
+}
+
+/* RESPONSIVE LAYOUT BREAKPOINTS MATRIX */
+@media (max-width: 1024px) {
+  .header-max-width-container { padding: 0 24px; }
+  .header-center-search-block { max-width: 20rem; }
+  .nav-link-item { padding: 6px 12px; font-size: 0.8rem; }
+}
+
+@media (max-width: 768px) {
+  .navigation-capsule-links { display: none; }
+  .header-center-search-block { display: none; }
+}
+
+/* Interactive cursor updates and transition indicators over navigation trigger anchors */
+.user-meta-profile-box {
+  cursor: pointer;
+  transition: background-color 200ms ease, border-color 200ms ease;
+  user-select: none;
+}
+
+.user-meta-profile-box:hover, .dropdown-active {
+  background-color: rgba(255, 255, 255, 0.05);
+  border-color: #7E5BD8;
+}
+
+/* Dynamic CSS smooth rotation for chevron status vectors */
+.chevron-down-vector {
+  transition: transform 200ms ease;
+}
+
+.rotate-chevron {
+  transform: rotate(180deg);
+  color: #00d2ff;
+}
+
+/* ==========================================================================
+   NEW DROPDOWN LAYER: Sleek neon floating operational profile menu matrix
+   ========================================================================== */
+.profile-context-dropdown-menu {
+  position: absolute;
+  top: calc(100% + 12px);
+  right: 0;
+  width: 240px;
+  background-color: rgba(15, 15, 26, 0.95);
+  border: 1px solid #1f1f2e;
+  border-bottom: 2px solid #7E5BD8;
+  border-radius: 0.75rem;
+  padding: 12px 0;
+  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.6), 0 0 15px rgba(126, 91, 216, 0.15);
+  backdrop-filter: blur(10px);
+  z-index: 1000;
+  display: flex;
+  flex-direction: column;
+  animation: dropdownSlideFade 150ms ease-out forwards;
+}
+
+@keyframes dropdownSlideFade {
+  from { opacity: 0; transform: translateY(-8px); }
+  to { opacity: 1; transform: translateY(0); }
+}
+
+.dropdown-header-identity {
+  padding: 8px 16px;
+  display: flex;
+  flex-direction: column;
+}
+
+.dropdown-user-name {
+  font-size: 0.9rem;
+  font-weight: 600;
+  color: #ffffff;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.dropdown-user-role {
+  font-size: 0.75rem;
+  font-weight: 700;
+  color: #00d2ff;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  margin-top: 2px;
+}
+
+.dropdown-divider-line {
+  height: 1px;
+  background-color: rgba(255, 255, 255, 0.05);
+  margin: 8px 0;
+}
+
+.dropdown-menu-item {
+  padding: 10px 16px;
+  font-size: 0.85rem;
+  font-weight: 500;
+  color: #a0a0b8;
+  text-decoration: none;
+  background: transparent;
+  border: none;
+  text-align: left;
+  width: 100%;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  transition: color 150ms ease, background-color 150ms ease;
+}
+
+.dropdown-menu-item:hover {
+  color: #ffffff;
+  background-color: rgba(255, 255, 255, 0.03);
+}
+
+.logout-action-trigger {
+  color: #ef4444;
+}
+
+.logout-action-trigger:hover {
+  color: #ff6b6b;
+  background-color: rgba(239, 68, 68, 0.05);
+}

+ 94 - 0
final project/web-game-shop/src/app/components/header/header.html

@@ -0,0 +1,94 @@
+<header class="header-global-navigation">
+  <div class="header-max-width-container">
+
+    <div class="header-left-block">
+      <a routerLink="/storefront" class="brand-identity-anchor">
+        <div class="brand-gradient-logo-box">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="logo-svg-vector" fill="currentColor" aria-hidden="true">
+            <path d="M448 64c106 0 192 86 192 192S554 448 448 448l-256 0C86 448 0 362 0 256S86 64 192 64l256 0zM192 176c-13.3 0-24 10.7-24 24l0 32-32 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l32 0 0 32c0 13.3 10.7 24 24 24s24-10.7 24-24l0-32 32 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-32 0 0-32c0-13.3-10.7-24-24-24zm240 96a32 32 0 1 0 0 64 32 32 0 1 0 0-64zm64-96a32 32 0 1 0 0 64 32 32 0 1 0 0-64z"/></svg>
+        </div>
+        <span class="brand-name-text">NEXUS</span>
+      </a>
+
+      <nav class="navigation-capsule-links">
+        <a routerLink="/storefront" routerLinkActive="active-route" class="nav-link-item">Storefront</a>
+
+        <a (click)="onNavigateToLastViewedGame()" style="cursor: pointer;" class="nav-link-item">Game Details</a>
+
+        <a routerLink="/profile" routerLinkActive="active-route" class="nav-link-item">My Profile</a>
+        <a routerLink="/library" routerLinkActive="active-route" class="nav-link-item">Library</a>
+        <a routerLink="/bug-report" routerLinkActive="active-route" class="nav-link-item">Bug reports</a>
+      </nav>
+    </div>
+
+    <div class="header-center-search-block">
+      <div class="search-field-capsule">
+        <div class="search-magnifier-icon-box" (click)="onSearch()" style="cursor: pointer;" title="Click to search">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="search-svg" fill="currentColor" aria-hidden="true">
+            <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
+          </svg>
+        </div>
+        <input type="text"
+               [(ngModel)]="searchQuery"
+               (keyup.enter)="onSearch()"
+               placeholder="Search over 10,000+ games..."
+               class="search-native-input">
+        <kbd class="search-hotkey-badge" (click)="onSearch()" style="cursor: pointer; user-select: none;" title="Click to search">search</kbd>
+      </div>
+    </div>
+
+    <div class="header-right-auth-block">
+
+      @if (!isLoggedIn) {
+        <div class="guest-actions-group">
+          <a routerLink="/login" class="link-sign-in-btn">Sign In</a>
+          <a routerLink="/register" class="link-join-nexus-btn">Join NEXUS</a>
+        </div>
+      } @else {
+        <div class="authorized-profile-badge-row">
+
+          <div class="shopping-cart-wrapper" routerLink="/cart" style="cursor: pointer;" title="NEXUS Core Cart Workspace">
+            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="cart-svg" fill="none" aria-hidden="true">
+              <path d="M5.00014 14H18.1359C19.1487 14 19.6551 14 20.0582 13.8112C20.4134 13.6448 20.7118 13.3777 20.9163 13.0432C21.1485 12.6633 21.2044 12.16 21.3163 11.1534L21.9013 5.88835C21.9355 5.58088 21.9525 5.42715 21.9031 5.30816C21.8597 5.20366 21.7821 5.11697 21.683 5.06228C21.5702 5 21.4155 5 21.1062 5H4.50014M2 2H3.24844C3.51306 2 3.64537 2 3.74889 2.05032C3.84002 2.09463 3.91554 2.16557 3.96544 2.25376C4.02212 2.35394 4.03037 2.48599 4.04688 2.7501L4.95312 17.2499C4.96963 17.514 4.97788 17.6461 5.03456 17.7462C5.08446 17.8344 5.15998 17.9054 5.25111 17.9497C5.35463 18 5.48694 18 5.75156 18H19M7.5 21.5H7.51M16.5 21.5H16.51M8 21.5C8 21.7761 7.77614 22 7.5 22C7.22386 22 7 21.7761 7 21.5C7 21.2239 7.22386 21 7.5 21C7.77614 21 8 21.2239 8 21.5ZM17 21.5C17 21.7761 16.7761 22 16.5 22C16.2239 22 16 21.7761 16 21.5C16 21.2239 16.2239 21 16.5 21C16.7761 21 17 21.2239 17 21.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+            </svg>
+            <span class="cart-items-counter-badge">{{ cartItemCount }}</span>
+          </div>
+
+          <div class="user-meta-profile-box-wrapper" style="position: relative;">
+            <div class="user-meta-profile-box" (click)="toggleDropdown($event)" [class.dropdown-active]="isDropdownOpen">
+              <div class="text-metadata-stack">
+                <p class="user-profile-fullname">{{ currentUser.fullName }}</p>
+                <p class="user-profile-role-status">{{ currentUser.role }}</p>
+              </div>
+              <img [src]="currentUser.avatarUrl" alt="User Authorized Profile Image Avatar" class="profile-avatar-img">
+              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="chevron-down-vector" fill="currentColor" [class.rotate-chevron]="isDropdownOpen">
+                <path d="M201.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 338.7 54.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/>
+              </svg>
+            </div>
+
+            <div class="profile-context-dropdown-menu" *ngIf="isDropdownOpen">
+              <div class="dropdown-header-identity">
+                <span class="dropdown-user-name">{{ currentUser.fullName }}</span>
+                <span class="dropdown-user-role">{{ currentUser.role }}</span>
+              </div>
+              <div class="dropdown-divider-line"></div>
+              <a routerLink="/profile" class="dropdown-menu-item" (click)="isDropdownOpen = false">
+                👤 My Profile Matrix
+              </a>
+              <a routerLink="/library" class="dropdown-menu-item" (click)="isDropdownOpen = false">
+                🎮 Stored Core Licenses
+              </a>
+              <div class="dropdown-divider-line"></div>
+              <button class="dropdown-menu-item logout-action-trigger" (click)="logoutSession()">
+                ❌ Disconnect Session
+              </button>
+            </div>
+          </div>
+
+        </div>
+      }
+
+    </div>
+
+  </div>
+</header>

+ 21 - 0
final project/web-game-shop/src/app/components/header/header.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import {HeaderComponent } from './header';
+
+describe('Header', () => {
+  let component: HeaderComponent;
+  let fixture: ComponentFixture<HeaderComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [HeaderComponent],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(HeaderComponent);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 117 - 0
final project/web-game-shop/src/app/components/header/header.ts

@@ -0,0 +1,117 @@
+import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule, Router } from '@angular/router';
+import { FormsModule } from '@angular/forms';
+import { UserService } from '../../services/user/user';
+import {BehaviorSubject, Subscription} from 'rxjs';
+import { CartService } from '../../services/cart/cart';
+
+@Component({
+  selector: 'app-header',
+  standalone: true,
+  imports: [CommonModule, RouterModule, FormsModule],
+  templateUrl: './header.html',
+  styleUrls: ['./header.css']
+})
+export class HeaderComponent implements OnInit, OnDestroy {
+  isLoggedIn: boolean = false;
+  searchQuery: string = '';
+  isDropdownOpen: boolean = false;
+  cartItemCount: number = 0; // Змінна для кількості ігор у кошику
+  private sessionSubscription!: Subscription;
+
+  currentUser = {
+    fullName: 'Anonymous Guest',
+    role: 'Viewer',
+    avatarUrl: 'https://storage.googleapis.com/uxpilot-auth.appspot.com/avatars/avatar-4.jpg'
+  };
+
+  private cartCountSubject = new BehaviorSubject<number>(0);
+  cartCount$ = this.cartCountSubject.asObservable();
+
+  updateCount(count: number): void {
+    this.cartCountSubject.next(count);
+  }
+
+
+  constructor(
+    private cdr: ChangeDetectorRef,
+    private router: Router,
+    private userService: UserService,
+    private cartService: CartService
+  ) {}
+
+  ngOnInit(): void {
+    this.sessionSubscription = this.userService.currentSession$.subscribe({
+      next: (session) => {
+        if (session) {
+          this.isLoggedIn = true;
+          this.currentUser.fullName = session.fullName;
+          this.currentUser.role = session.role || 'Customer';
+        } else {
+          this.isLoggedIn = false;
+          this.currentUser.fullName = 'Anonymous Guest';
+          this.currentUser.role = 'Viewer';
+        }
+        this.updateCartCount();
+        this.cdr.detectChanges();
+      }
+    });
+
+    // Subscribing to global cart count stream
+    this.cartService.cartCount$.subscribe(count => {
+      this.cartItemCount = count;
+      this.cdr.detectChanges();
+    });
+
+    window.addEventListener('storage', (event) => {
+      if (event.key === 'nexus_cart_items') {
+        this.updateCartCount();
+        this.cdr.detectChanges();
+      }
+    });
+  }
+
+  updateCartCount(): void {
+    const cartItems = JSON.parse(localStorage.getItem('nexus_cart_items') || '[]');
+    this.cartItemCount = Array.isArray(cartItems) ? cartItems.length : 0;
+  }
+
+  onNavigateToLastViewedGame(): void {
+    const lastGameId = localStorage.getItem('nexus_last_viewed_game_id');
+
+    if (lastGameId) {
+      this.router.navigate(['/games', lastGameId]);
+    } else {
+      alert("No data matrix found. Please select a game title sequence from the Catalog storefront first.");
+      this.router.navigate(['/storefront']);
+    }
+  }
+
+  ngOnDestroy(): void {
+    if (this.sessionSubscription) {
+      this.sessionSubscription.unsubscribe();
+    }
+  }
+
+  toggleDropdown(event: MouseEvent): void {
+    event.stopPropagation();
+    this.isDropdownOpen = !this.isDropdownOpen;
+  }
+
+  logoutSession(): void {
+    this.userService.clearSessionState();
+    this.isDropdownOpen = false;
+    this.router.navigate(['/login']);
+  }
+
+  onSearch(): void {
+    console.log('Executing search sequence across backend dataset for query:', this.searchQuery);
+
+    // Redirect context to storefront routing space while passing the active query token variables
+    this.router.navigate(['/storefront'], {
+      queryParams: { search: this.searchQuery },
+      queryParamsHandling: 'merge' // Keeps other query params if any exist
+    });
+  }
+}

+ 142 - 0
final project/web-game-shop/src/app/components/library/library.css

@@ -0,0 +1,142 @@
+.library-container {
+  max-width: 1200px;
+  margin-top: 40px;
+  padding: 40px 20px;
+  color: #ffffff;
+  font-family: 'Rajdhani', sans-serif;
+}
+
+.library-header h1 {
+  font-size: 2.5rem;
+  font-weight: 700;
+  color: #ffffff;
+  margin-bottom: 8px;
+}
+
+.library-header p {
+  color: #a0a0b8;
+  font-size: 1rem;
+  margin-bottom: 32px;
+}
+
+.alert {
+  padding: 12px 16px;
+  border-radius: 8px;
+  margin-bottom: 24px;
+  font-size: 0.95rem;
+}
+
+.alert-success {
+  background-color: rgba(16, 185, 129, 0.15);
+  border: 1px solid #10b981;
+  color: #34d399;
+}
+
+.alert-danger {
+  background-color: rgba(239, 68, 68, 0.15);
+  border: 1px solid #ef4444;
+  color: #f87171;
+}
+
+.empty-state {
+  text-align: center;
+  padding: 64px 0;
+  color: #a0a0b8;
+  font-size: 1.2rem;
+}
+
+.games-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 24px;
+}
+
+.game-card {
+  background-color: #1a1a2e;
+  border-radius: 12px;
+  overflow: hidden;
+  border: 1px solid #2d2d4a;
+  display: flex;
+  flex-direction: column;
+  transition: transform 0.2s ease, border-color 0.2s ease;
+}
+
+.game-card:hover {
+  transform: translateY(-4px);
+  border-color: #4f46e5;
+}
+
+.game-image-wrapper {
+  width: 100%;
+  height: 160px;
+  overflow: hidden;
+}
+
+.game-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.game-info {
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+}
+
+.game-info h3 {
+  font-size: 1.5rem;
+  margin: 0 0 4px 0;
+  color: #ffffff;
+}
+
+.purchase-date {
+  color: #8585a0;
+  font-size: 0.85rem;
+  margin-bottom: 8px;
+}
+
+.game-price {
+  font-size: 1.2rem;
+  font-weight: 700;
+  color: #34d399;
+  margin-bottom: 16px;
+}
+
+.card-actions {
+  margin-top: auto;
+  display: flex;
+  gap: 10px;
+}
+
+.btn {
+  flex: 1;
+  padding: 10px 0;
+  border-radius: 6px;
+  font-weight: 700;
+  font-size: 0.95rem;
+  border: none;
+  cursor: pointer;
+  transition: background-color 0.2s, opacity 0.2s;
+  font-family: 'Rajdhani', sans-serif;
+}
+
+.btn-primary {
+  background-color: #4f46e5;
+  color: #ffffff;
+}
+
+.btn-primary:hover {
+  background-color: #4338ca;
+}
+
+.btn-danger {
+  background-color: #33334d;
+  color: #ef4444;
+  border: 1px solid #444466;
+}
+
+.btn-danger:hover {
+  background-color: rgba(239, 68, 68, 0.1);
+}

+ 42 - 0
final project/web-game-shop/src/app/components/library/library.html

@@ -0,0 +1,42 @@
+<div class="library-container">
+  <div class="library-header">
+    <h1>My Library</h1>
+    <p>Manage your collection, track playtime, and launch games.</p>
+  </div>
+
+  <div *ngIf="successMessage" class="alert alert-success">
+    {{ successMessage }}
+  </div>
+  <div *ngIf="errorMessage" class="alert alert-danger">
+    {{ errorMessage }}
+  </div>
+
+  <div *ngIf="items.length === 0" class="empty-state">
+    <p>No games in your library yet. Visit the Storefront to purchase games.</p>
+  </div>
+
+  <div class="games-grid" *ngIf="items.length > 0">
+    <div class="game-card" *ngFor="let item of items">
+      <div class="game-image-wrapper">
+        <img [src]="'http://localhost:8085' + item.game.imagePath" [alt]="item.game.title" class="game-image" />
+      </div>
+      <div class="game-info">
+        <h3>{{ item.game.title }}</h3>
+        <p class="purchase-date">Purchased: {{ item.lastPlayedAt | date }}</p>
+        <p class="game-price">{{ item.game.price | currency }}</p>
+
+        <div class="card-actions">
+          <button class="btn btn-primary" (click)="launchGame(item.game.title)">
+            Launch Game
+          </button>
+          <button class="btn btn-danger" (click)="returnGame(item.itemId, item.game.title)">
+            Return & Refund
+          </button>
+          <button class="btn btn-secondary" (click)="openReportPage(item.game.gameId)">
+            Report Bug
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 22 - 0
final project/web-game-shop/src/app/components/library/library.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { Library } from './library';
+
+describe('Library', () => {
+  let component: Library;
+  let fixture: ComponentFixture<Library>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [Library],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(Library);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 87 - 0
final project/web-game-shop/src/app/components/library/library.ts

@@ -0,0 +1,87 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { LibraryService } from '../../services/library/library';
+import { UserService } from '../../services/user/user';
+import { Router } from '@angular/router';
+
+@Component({
+  selector: 'app-library',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './library.html',
+  styleUrls: ['./library.css']
+})
+export class LibraryComponent implements OnInit {
+  // List of library items containing the game details
+  items: any[] = [];
+
+  currentUserId: number = 1;
+
+  successMessage: string | null = null;
+  errorMessage: string | null = null;
+  isProcessing: boolean = false;
+
+  constructor(
+    private libraryService: LibraryService,
+    private userService: UserService,
+    private cdr: ChangeDetectorRef,
+    private router: Router
+  ) {}
+
+  ngOnInit(): void {
+    const session = JSON.parse(localStorage.getItem('nexus_mock_session') || '{}');
+    if (session && session.userId) {
+      this.currentUserId = session.userId;
+    }
+
+    this.loadUserLibrary();
+  }
+
+  loadUserLibrary(): void {
+    this.libraryService.getUserLibrary(this.currentUserId).subscribe({
+      next: (data: any[]) => {
+        this.items = data;
+        this.cdr.detectChanges();
+      },
+      error: (err) => {
+        console.error('Failed to load library', err);
+        this.errorMessage = 'Failed to load your game library.';
+        this.cdr.detectChanges();
+      }
+    });
+  }
+
+  openReportPage(gameId: number): void {
+    localStorage.setItem('selected_game_id', gameId.toString());
+    this.router.navigate(['/bug-report']);
+  }
+
+  /**
+   * Returns a game using its unique library item ID.
+   */
+  returnGame(itemId: number | undefined, title: string): void {
+    if (this.isProcessing || itemId === undefined) return;
+
+    this.isProcessing = true;
+    this.successMessage = null;
+    this.errorMessage = null;
+
+    this.libraryService.refundGame(this.currentUserId, itemId).subscribe({
+      next: (response) => {
+        this.successMessage = `SUCCESS: Game "${title}" successfully returned. Funds refunded to wallet.`;
+        this.loadUserLibrary();
+        this.isProcessing = false;
+      },
+      error: (err) => {
+        console.error('Refund transaction faulted:', err);
+        this.errorMessage = `REFUND FAILED: Could not process return for "${title}".`;
+        this.isProcessing = false;
+        this.cdr.detectChanges();
+      }
+    });
+  }
+
+  launchGame(title: string): void {
+    alert(`Launching ${title}...`);
+  }
+}

+ 324 - 0
final project/web-game-shop/src/app/components/login/login.css

@@ -0,0 +1,324 @@
+/* Core viewport and baseline coordinates layout framework */
+.auth-page-container {
+  min-height: 100vh;
+  width: 100%;
+  background-color: #0b0b11;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+  font-family: 'Inter', sans-serif;
+  overflow-x: hidden;
+}
+
+.auth-split-matrix-layout {
+  display: flex;
+  width: 100%;
+  min-height: 100vh;
+  /* Translucent overlay background for the whole split split screen framework */
+  background-color: rgba(20, 20, 30, 0.2);
+}
+
+/* ==========================================================================
+   LEFT PANEL STYLES: Now styled directly as the floating layout card container
+   ========================================================================== */
+.auth-left-brand-panel {
+  flex: 1.2;
+  /* Seamless blending: deep radial gradient fade over the core image texture asset */
+  background-image:
+    linear-gradient(135deg, rgba(15, 15, 22, 0.85) 0%, rgba(11, 11, 17, 0.92) 100%),
+    url('../../assets/background-register-login.png');
+  background-size: cover;
+  background-position: center center;
+  background-repeat: no-repeat;
+
+  /*border-right: 1px solid rgba(255, 255, 255, 0.05);*/
+  padding: 60px 80px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  box-sizing: border-box;
+  position: relative;
+  overflow: hidden;
+}
+
+/* Enhancing cosmic atmospheric light projection effect matching Webflow specifications */
+.auth-left-brand-panel::before {
+  content: '';
+  position: absolute;
+  top: -10%;
+  left: -10%;
+  width: 300px;
+  height: 300px;
+  background-color: rgba(126, 91, 216, 0.15); /* Soft ambient nebula reflection purple */
+  border-radius: 50%;
+  filter: blur(80px);
+  pointer-events: none;
+  z-index: 1;
+}
+
+/* Ensure all semantic components inside the left sidebar sit safely above the ambient backdrop mask layer */
+.brand-identity-anchor,
+.heading-text-white,
+.paragraph-text-nexus-muted,
+.feature-bullets-stack-group {
+  position: relative;
+  z-index: 2;
+}
+
+@media (max-width: 960px) {
+  .auth-left-brand-panel { display: none; } /* Adaptive responsive collapse layout grid rule */
+}
+
+.brand-identity-anchor {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 48px;
+  text-decoration: none;
+}
+
+.brand-gradient-logo-box {
+  width: 44px;
+  height: 44px;
+  border-radius: 0.75rem;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-image: linear-gradient(to bottom right, #7E5BD8, #00d2ff); /* Blueprint gradient track config */
+  color: #ffffff;
+  box-shadow: 0 0 20px rgba(126, 91, 216, 0.4);
+}
+
+.logo-svg-vector {
+  width: 30px;
+  height: 30px;
+}
+
+.brand-name-text {
+  font-family: 'Rajdhani', sans-serif;
+  font-weight: 700;
+  font-size: 1.875rem;
+  letter-spacing: 0.1em;
+  color: #ffffff;
+}
+
+.heading-text-white {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 3.75rem;
+  font-weight: 700;
+  line-height: 1.25;
+  color: #ffffff;
+  margin: 0 0 16px 0;
+}
+
+.gradient-accent-text {
+  background-image: linear-gradient(to right, #7E5BD8, #00d2ff);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.paragraph-text-nexus-muted {
+  font-size: 1rem;
+  line-height: 1.625;
+  color: #a0a0b8; /* #a0a0b8 text layout palette marker */
+  max-width: 460px;
+  margin: 0 0 40px 0;
+}
+
+.feature-bullets-stack-group {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.bullet-row-item {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  background-color: rgba(26, 26, 46, 0.6); /* Transparent backdrop card wrapper background */
+  border: 1px solid #2a2a40;
+  border-radius: 9999px;
+  padding: 8px 16px;
+  width: fit-content;
+}
+
+.checkmark-icon-capsule {
+  color: #00ff88; /* Derived directly from dom-text-nexus green spec */
+  font-size: 12px;
+  font-weight: 700;
+}
+
+.bullet-label-span {
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: #ffffff;
+}
+
+/* ==========================================================================
+   RIGHT PANEL STYLES: Core credentials entry form module
+   ========================================================================== */
+.auth-right-credentials-panel {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 64px;
+  box-sizing: border-box;
+  background-color: #14141e;
+}
+
+.credentials-inner-capsule {
+  width: 100%;
+  max-width: 420px;
+}
+
+.auth-section-header-box {
+  margin-bottom: 32px;
+}
+
+.auth-main-title {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 1.875rem;
+  font-weight: 700;
+  color: #ffffff;
+  margin: 0 0 4px 0;
+}
+
+.auth-subtitle {
+  font-size: 0.875rem;
+  color: #a0a0b8;
+  margin: 0;
+}
+
+/* Form structure constraints blueprints mapping */
+.native-credential-form-element {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.input-field-group-stack {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.input-label-with-action-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.native-input-label {
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: #a0a0b8;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+}
+
+.inner-form-forgot-link {
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: #a27cff; /* Styled to matching link-text-nexus-accent-glow configuration token */
+  text-decoration: none;
+}
+
+.inner-form-forgot-link:hover {
+  text-decoration: underline;
+}
+
+.native-form-text-input {
+  width: 100%;
+  background-color: #0f0f16; /* Dark field canvas color #0f0f16 */
+  border: 1px solid #2a2a40;
+  border-radius: 0.75rem;
+  padding: 14px 16px;
+  font-size: 0.875rem;
+  color: #ffffff;
+  outline: none;
+  box-sizing: border-box;
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.native-form-text-input:focus {
+  border-color: #7E5BD8;
+  box-shadow: 0 0 0 2px rgba(126, 91, 216, 0.4);
+}
+
+.input-validation-error {
+  border-color: #ef4444 !important;
+}
+
+.validation-tip-msg {
+  font-size: 0.775rem;
+  color: #ef4444;
+}
+
+/* Gradient primary authentication trigger controller */
+.action-submit-btn-gradient {
+  width: 100%;
+  padding: 14px;
+  border: none;
+  border-radius: 0.75rem;
+  background-image: linear-gradient(to right, #7E5BD8, #00d2ff); /* Classic linear layout spec wrapper mapping */
+  color: #ffffff;
+  font-size: 0.875rem;
+  font-weight: 700;
+  cursor: pointer;
+  box-shadow: 0 0 20px rgba(126, 91, 216, 0.4);
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+  margin-top: 10px;
+}
+
+.action-submit-btn-gradient:hover:not(:disabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 0 30px rgba(126, 91, 216, 0.6);
+}
+
+.action-submit-btn-gradient:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+  transform: none;
+  box-shadow: none;
+}
+
+/* Bottom footer interface redirects link wrappers */
+.auth-footer-redirect-msg {
+  text-align: center;
+  font-size: 0.75rem;
+  color: #a0a0b8;
+  margin-top: 24px;
+}
+
+.redirect-hyperlink-token {
+  color: #a27cff;
+  text-decoration: none;
+  font-weight: 500;
+}
+
+.redirect-hyperlink-token:hover {
+  text-decoration: underline;
+}
+
+/* System Alerts Feedback Elements Mappings */
+.alert-banner {
+  padding: 12px 16px;
+  border-radius: 0.5rem;
+  font-size: 0.875rem;
+  margin-bottom: 20px;
+  font-weight: 500;
+}
+
+.alert-success {
+  background-color: rgba(0, 255, 136, 0.1);
+  color: #00ff88;
+  border: 1px solid rgba(0, 255, 136, 0.2);
+}
+
+.alert-danger {
+  background-color: rgba(239, 68, 68, 0.1);
+  color: #ef4444;
+  border: 1px solid rgba(239, 68, 68, 0.2);
+}

+ 92 - 0
final project/web-game-shop/src/app/components/login/login.html

@@ -0,0 +1,92 @@
+<div class="auth-page-container">
+  <div class="auth-split-matrix-layout">
+
+    <div class="auth-left-brand-panel">
+      <div class="brand-identity-anchor">
+        <div class="brand-gradient-logo-box">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="logo-svg-vector" fill="currentColor">
+            <!--!Font Awesome Free v7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.-->
+            <path d="M448 64c106 0 192 86 192 192S554 448 448 448l-256 0C86 448 0 362 0 256S86 64 192 64l256 0zM192 176c-13.3 0-24 10.7-24 24l0 32-32 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l32 0 0 32c0 13.3 10.7 24 24 24s24-10.7 24-24l0-32 32 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-32 0 0-32c0-13.3-10.7-24-24-24zm240 96a32 32 0 1 0 0 64 32 32 0 1 0 0-64zm64-96a32 32 0 1 0 0 64 32 32 0 1 0 0-64z"/></svg>
+        </div>
+        <span class="brand-name-text">NEXUS</span>
+      </div>
+
+      <h2 class="heading-text-white">Your Universe.<br><span class="gradient-accent-text">One Platform.</span></h2>
+      <p class="paragraph-text-nexus-muted">Buy, play, and track your favorite games. Report bugs, manage your library, and connect with developers all in one place.</p>
+
+      <div class="feature-bullets-stack-group">
+        <div class="bullet-row-item">
+          <div class="checkmark-icon-capsule">✓</div>
+          <span class="bullet-label-span">Game Store Integration</span>
+        </div>
+        <div class="bullet-row-item">
+          <div class="checkmark-icon-capsule">✓</div>
+          <span class="bullet-label-span">Your Dynamic Library</span>
+        </div>
+        <div class="bullet-row-item">
+          <div class="checkmark-icon-capsule">✓</div>
+          <span class="bullet-label-span">Integrated Bug Tracker</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="auth-right-credentials-panel">
+      <div class="credentials-inner-capsule">
+
+        <div class="auth-section-header-box">
+          <h1 class="auth-main-title">Sign in to Nexus</h1>
+          <p class="auth-subtitle">Welcome back! Please enter your access parameters</p>
+        </div>
+
+        @if (successMessage) {
+          <div class="alert-banner alert-success">{{ successMessage }}</div>
+        }
+        @if (errorMessage) {
+          <div class="alert-banner alert-danger">{{ errorMessage }}</div>
+        }
+
+        <form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="native-credential-form-element">
+
+          <div class="input-field-group-stack">
+            <label class="native-input-label">Email Address</label>
+            <input type="email"
+                   formControlName="email"
+                   placeholder="name@domain.com"
+                   class="native-form-text-input"
+                   [class.input-validation-error]="loginForm.get('email')?.invalid && loginForm.get('email')?.touched">
+            @if (loginForm.get('email')?.invalid && loginForm.get('email')?.touched) {
+              <span class="validation-tip-msg">Please assign a valid active electronic address format.</span>
+            }
+          </div>
+
+          <div class="input-field-group-stack">
+            <div class="input-label-with-action-row">
+              <label class="native-input-label">Password</label>
+              <a routerLink="/forgot-password" class="inner-form-forgot-link">Forgot password?</a>
+            </div>
+            <input type="password"
+                   formControlName="password"
+                   placeholder="••••••••"
+                   class="native-form-text-input"
+                   [class.input-validation-error]="loginForm.get('password')?.invalid && loginForm.get('password')?.touched">
+            @if (loginForm.get('password')?.invalid && loginForm.get('password')?.touched) {
+              <span class="validation-tip-msg">Password validation demands minimum 8 tokens.</span>
+            }
+          </div>
+
+          <button type="submit"
+                  [disabled]="loginForm.invalid || isSubmitting"
+                  class="action-submit-btn-gradient">
+            {{ isSubmitting ? 'Verifying Identity...' : 'Sign In' }}
+          </button>
+        </form>
+
+        <p class="auth-footer-redirect-msg">
+          Don't have an account yet? <a routerLink="/register" class="redirect-hyperlink-token">Create Account</a>
+        </p>
+
+      </div>
+    </div>
+
+  </div>
+</div>

+ 22 - 0
final project/web-game-shop/src/app/components/login/login.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login';
+
+describe('Login', () => {
+  let component: LoginComponent;
+  let fixture: ComponentFixture<LoginComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [LoginComponent],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(LoginComponent);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 96 - 0
final project/web-game-shop/src/app/components/login/login.ts

@@ -0,0 +1,96 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
+import { Router, RouterLink } from '@angular/router';
+import { UserService } from '../../services/user/user';
+
+@Component({
+  selector: 'app-login',
+  standalone: true,
+  imports: [ReactiveFormsModule, RouterLink],
+  templateUrl: './login.html',
+  styleUrls: ['./login.css']
+})
+export class LoginComponent implements OnInit {
+  loginForm!: FormGroup;
+  isSubmitting = false;
+  errorMessage: string | null = null;
+  successMessage: string | null = null;
+
+  constructor(
+    private fb: FormBuilder,
+    private router: Router,
+    private userService: UserService
+  ) {}
+
+  ngOnInit(): void {
+    // Initialize the credentials reactive form with schema constraints validation
+    this.loginForm = this.fb.group({
+      email: ['', [Validators.required, Validators.email]],
+      password: ['', [Validators.required, Validators.minLength(8)]]
+    });
+  }
+
+  onSubmit(): void {
+    // Abort sequence if form values do not satisfy validation parameters
+    if (this.loginForm.invalid) {
+      this.loginForm.markAllAsTouched();
+      return;
+    }
+
+    this.isSubmitting = true;
+    this.errorMessage = null;
+    this.successMessage = null;
+
+    // Map 'password' parameter exactly as expected by Spring Boot LoginRequest DTO
+    const credentials = {
+      email: this.loginForm.value.email,
+      password: this.loginForm.value.password
+    };
+
+    // Dispatch authentication request packet to the remote security endpoint
+    this.userService.login(credentials).subscribe({
+      next: (authenticatedUser: any) => {
+        this.isSubmitting = false;
+        this.successMessage = 'Authentication successful! Synchronizing configuration rules...';
+
+        // Persist the JWT bearer token for the authInterceptor pipeline mapping
+        if (authenticatedUser && authenticatedUser.token) {
+          localStorage.setItem('nexus_auth_token', authenticatedUser.token);
+        }
+
+        // Cache session parameters required by ProfileComponent profile dashboard layer
+        localStorage.setItem('nexus_mock_session', JSON.stringify({
+          userId: authenticatedUser.userId,
+          fullName: authenticatedUser.fullName,
+          role: authenticatedUser.role
+        }));
+
+        // Propagate updated authentication state to subscribers via shared service context
+        if (typeof this.userService.updateSessionState === 'function') {
+          this.userService.updateSessionState({
+            userId: authenticatedUser.userId,
+            fullName: authenticatedUser.fullName,
+            role: authenticatedUser.role
+          });
+        }
+
+        // Trigger safe delayed routing transaction to avoid UI thread collision
+        setTimeout(() => {
+          const targetRole = authenticatedUser.role ? authenticatedUser.role.toUpperCase() : '';
+
+          // Route user state node to storefront stream view
+          if (targetRole === 'DEVELOPER') {
+            this.router.navigate(['/storefront']);
+          } else {
+            this.router.navigate(['/storefront']);
+          }
+        }, 1500);
+      },
+      error: (err) => {
+        this.isSubmitting = false;
+        console.error('Login request sequence rejected:', err);
+        this.errorMessage = 'Invalid credentials or connection boundary fault.';
+      }
+    });
+  }
+}

+ 411 - 0
final project/web-game-shop/src/app/components/profile/profile.css

@@ -0,0 +1,411 @@
+/* Core operational dashboard bounds layout wrapper grid mapping background colors */
+.profile-dashboard-container {
+  min-height: 90vh;
+  width: 100%;
+  padding: 60px 40px;
+  margin-top: 30px;
+  box-sizing: border-box;
+  background-color: #14141e;
+  font-family: 'Inter', sans-serif;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.profile-matrix-grid {
+  display: flex;
+  gap: 40px;
+  width: 100%;
+  max-width: 1140px;
+  align-items: stretch;
+}
+
+@media (max-width: 920px) {
+  .profile-matrix-grid {
+    flex-direction: column;
+  }
+}
+
+/* ==========================================================================
+   LEFT COMPONENT: Sleek profile avatar identification shield matrix
+   ========================================================================== */
+.profile-card-identity-shield {
+  flex: 0.8;
+  background-color: rgba(15, 15, 26, 0.85);
+  border: 4px solid #1f1f2e;
+  border-radius: 1.5rem;
+  padding: 48px 32px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
+  backdrop-filter: blur(12px);
+}
+
+.user-avatar-neon-capsule {
+  width: 110px;
+  height: 110px;
+  border-radius: 50%;
+  background-image: linear-gradient(to bottom right, #7E5BD8, #00d2ff);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  box-shadow: 0 0 25px rgba(126, 91, 216, 0.5);
+  margin-bottom: 24px;
+}
+
+.avatar-initials-token {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 3rem;
+  font-weight: 700;
+  color: #ffffff;
+}
+
+.active-status-pulse-dot {
+  width: 16px;
+  height: 16px;
+  background-color: #00ff88;
+  border: 3px solid #0f0f1a;
+  border-radius: 50%;
+  position: absolute;
+  bottom: 4px;
+  right: 4px;
+  box-shadow: 0 0 10px #00ff88;
+}
+
+.user-display-name-text {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 1.875rem;
+  font-weight: 700;
+  color: #ffffff;
+  margin: 0 0 8px 0;
+}
+
+.role-badge-capsule {
+  font-size: 0.75rem;
+  font-weight: 700;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
+  padding: 6px 16px;
+  border-radius: 9999px;
+  margin-bottom: 32px;
+}
+
+.badge-customer {
+  background-color: rgba(0, 210, 255, 0.1);
+  color: #00d2ff;
+  border: 1px solid rgba(0, 210, 255, 0.2);
+}
+
+.badge-developer {
+  background-color: rgba(126, 91, 216, 0.15);
+  color: #a27cff;
+  border: 1px solid rgba(126, 91, 216, 0.3);
+}
+
+.meta-info-separator-line {
+  width: 100%;
+  height: 1px;
+  background-color: rgba(255, 255, 255, 0.05);
+  margin-bottom: 28px;
+}
+
+.user-system-metadata-stack {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.metadata-row-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 0.85rem;
+}
+
+.meta-label {
+  color: #a0a0b8;
+  font-weight: 500;
+}
+
+.meta-value {
+  color: #ffffff;
+  font-weight: 600;
+}
+
+.meta-value-email {
+  color: #ffffff;
+  font-weight: 500;
+  word-break: break-all;
+  max-width: 170px;
+  text-align: right;
+}
+
+.meta-value-status {
+  color: #00ff88;
+  font-weight: 700;
+  text-transform: uppercase;
+}
+
+/* ==========================================================================
+   RIGHT COMPONENT: Financial ledger reporting board framework
+   ========================================================================== */
+.profile-details-ledger-panel {
+  flex: 1.4;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.ledger-header-box {
+  margin-bottom: 28px;
+}
+
+.ledger-main-title {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 2.25rem;
+  font-weight: 700;
+  color: #ffffff;
+  margin: 0 0 6px 0;
+}
+
+.ledger-subtitle {
+  font-size: 0.95rem;
+  color: #a0a0b8;
+  margin: 0;
+}
+
+.financial-cards-row-grid {
+  display: flex;
+  gap: 24px;
+  margin-bottom: 32px;
+}
+
+@media (max-width: 640px) {
+  .financial-cards-row-grid {
+    flex-direction: column;
+  }
+}
+
+.financial-metric-card-box {
+  flex: 1;
+  background-color: #0f0f16;
+  border-radius: 1rem;
+  padding: 28px;
+  box-sizing: border-box;
+  position: relative;
+  transition: transform 150ms ease;
+}
+
+.asset-glow-purple { border: 1px solid #2a2a40; }
+.asset-glow-purple::before {
+  content: ''; position: absolute; bottom: 0; left: 10%; width: 80%; height: 2px;
+  background-image: linear-gradient(to right, transparent, #7E5BD8, transparent);
+}
+
+.asset-glow-cyan { border: 1px solid #2a2a40; }
+.asset-glow-cyan::before {
+  content: ''; position: absolute; bottom: 0; left: 10%; width: 80%; height: 2px;
+  background-image: linear-gradient(to right, transparent, #00d2ff, transparent);
+}
+
+.metric-card-label {
+  font-size: 0.75rem;
+  font-weight: 600;
+  color: #a0a0b8;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  display: block;
+  margin-bottom: 12px;
+}
+
+.metric-card-value {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 2.5rem;
+  font-weight: 700;
+  margin: 0 0 12px 0;
+}
+
+.text-gradient-neon {
+  background-image: linear-gradient(to right, #b493ff, #7E5BD8);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.text-gradient-cyan {
+  background-image: linear-gradient(to right, #00d2ff, #00a4cc);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.metric-card-desc {
+  font-size: 0.8rem;
+  line-height: 1.4;
+  color: #7c7c94;
+  margin: 0;
+}
+
+/* Ecosystem Asset Analytics Panel Box element specs mappings */
+.ecosystem-analytics-panel-card {
+  background-color: rgba(26, 26, 46, 0.4);
+  border: 1px solid #2a2a40;
+  border-radius: 1rem;
+  padding: 32px;
+  box-sizing: border-box;
+}
+
+.analytics-card-title {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 1.25rem;
+  font-weight: 700;
+  color: #ffffff;
+  margin: 0 0 24px 0;
+  letter-spacing: 0.02em;
+  text-transform: uppercase;
+}
+
+.analytics-stats-split-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.analytics-stat-node {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+}
+
+.stat-node-counter {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 3rem;
+  font-weight: 700;
+  color: #ffffff;
+  line-height: 1;
+  margin-bottom: 8px;
+}
+
+.stat-node-description {
+  font-size: 0.8rem;
+  font-weight: 600;
+  color: #a0a0b8;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+}
+
+.analytics-vertical-divider {
+  width: 1px;
+  height: 60px;
+  background-color: rgba(255, 255, 255, 0.05);
+}
+
+/* Кнопки керування розробника */
+.dev-action-btn {
+  background: rgba(126, 91, 216, 0.1);
+  border: 1px solid #7E5BD8;
+  border-radius: 0.5rem;
+  padding: 10px 18px;
+  color: #a27cff;
+  font-weight: 700;
+  font-size: 0.85rem;
+  cursor: pointer;
+  transition: all 200ms ease;
+}
+
+.dev-action-btn:hover, .dev-action-btn.active {
+  background: #7E5BD8;
+  color: #ffffff;
+  box-shadow: 0 0 12px rgba(126, 91, 216, 0.4);
+}
+
+.dev-action-btn-danger {
+  background: rgba(239, 68, 68, 0.1);
+  border: 1px solid #ef4444;
+  border-radius: 0.5rem;
+  padding: 10px 18px;
+  color: #ef4444;
+  font-weight: 700;
+  font-size: 0.85rem;
+  cursor: pointer;
+  transition: all 200ms ease;
+}
+
+.dev-action-btn-danger:hover, .dev-action-btn-danger.active {
+  background: #ef4444;
+  color: #ffffff;
+  box-shadow: 0 0 12px rgba(239, 68, 68, 0.4);
+}
+
+/* Внутрішні панелі форм */
+.dev-sub-panel {
+  margin-top: 20px;
+  background-color: #0f0f16;
+  border: 1px solid #2a2a40;
+  border-radius: 0.75rem;
+  padding: 20px;
+  animation: fadeIn 250ms ease;
+}
+
+.dev-form-input {
+  width: 100%;
+  background: rgba(18, 18, 26, 0.8);
+  border: 1px solid #2a2a40;
+  border-radius: 0.5rem;
+  padding: 12px 16px;
+  color: #ffffff;
+  font-family: inherit;
+  font-size: 0.9rem;
+  outline: none;
+  box-sizing: border-box;
+  transition: border-color 200ms ease;
+}
+
+.dev-form-input:focus {
+  border-color: #7E5BD8;
+}
+
+textarea.dev-form-input {
+  resize: vertical;
+}
+
+select.dev-form-input option {
+  background-color: #14141e;
+  color: #ffffff;
+}
+
+/* Кнопки сабміту форм */
+.dev-submit-btn {
+  background: linear-gradient(90deg, #7E5BD8 0%, #6366f1 100%);
+  border: none;
+  border-radius: 0.5rem;
+  padding: 12px;
+  color: #ffffff;
+  font-weight: 700;
+  font-size: 0.9rem;
+  cursor: pointer;
+  transition: opacity 200ms ease;
+}
+
+.dev-submit-btn:hover {
+  opacity: 0.9;
+}
+
+.dev-submit-btn-danger {
+  background: #ef4444;
+  border: none;
+  border-radius: 0.5rem;
+  padding: 12px;
+  color: #ffffff;
+  font-weight: 700;
+  font-size: 0.9rem;
+  cursor: pointer;
+  transition: opacity 200ms ease;
+}
+
+.dev-submit-btn-danger:hover {
+  opacity: 0.9;
+}

+ 316 - 0
final project/web-game-shop/src/app/components/profile/profile.html

@@ -0,0 +1,316 @@
+<div class="profile-dashboard-container" *ngIf="isLoading">
+  <div class="ledger-header-box" style="text-align: center; width: 100%;">
+    <h1 class="ledger-main-title text-gradient-neon">Synchronizing Data Node...</h1>
+    <p class="ledger-subtitle">Establishing secure data stream connection with Nexus core telemetry server.</p>
+  </div>
+</div>
+
+<div class="profile-dashboard-container" *ngIf="!isLoading && currentUser">
+  <div class="profile-matrix-grid">
+
+    <div class="profile-card-identity-shield">
+      <div class="user-avatar-neon-capsule">
+        <span class="avatar-initials-token">{{ currentUser.fullName.charAt(0) || 'U' }}</span>
+        <div class="active-status-pulse-dot"></div>
+      </div>
+
+      <h2 class="user-display-name-text">{{ currentUser.fullName }}</h2>
+
+      <div class="role-badge-capsule" [ngClass]="getIsDeveloper() ? 'badge-developer' : 'badge-customer'">
+        {{ currentUser.role }}
+      </div>
+
+      <div class="meta-info-separator-line"></div>
+
+      <div class="user-system-metadata-stack" style="margin-bottom: 20px; background: rgba(255,255,255,0.02); padding: 14px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.05);">
+        <div class="metadata-row-item" style="padding: 4px 0;">
+          <span class="meta-label" style="color: #00d2ff; font-weight: bold;">Games Pool:</span>
+          <span class="meta-value">
+            {{ getIsDeveloper() ? myGames.length : myPurchasedGames.length }} Titles
+          </span>
+        </div>
+        <div class="metadata-row-item" *ngIf="!getIsDeveloper()" style="padding: 4px 0;">
+          <span class="meta-label" style="color: #ef4444; font-weight: bold;">Total Spent:</span>
+          <span class="meta-value" style="color: #ef4444;">
+            {{ currentUser.totalSpent | currency:'USD':'symbol':'1.2-2' }}
+          </span>
+        </div>
+        <div class="metadata-row-item" *ngIf="getIsDeveloper()" style="padding: 4px 0;">
+          <span class="meta-label" style="color: #10b981; font-weight: bold;">Total Revenue:</span>
+          <span class="meta-value" style="color: #10b981;">
+            {{ currentUser.walletBalance | currency:'USD':'symbol':'1.2-2' }}
+          </span>
+        </div>
+      </div>
+
+      <div class="meta-info-separator-line"></div>
+
+      <div class="user-system-metadata-stack">
+        <div class="metadata-row-item">
+          <span class="meta-label">Account Index:</span>
+          <span class="meta-value">#{{ currentUser.userId }}</span>
+        </div>
+        <div class="metadata-row-item">
+          <span class="meta-label">System Node Email:</span>
+          <span class="meta-value-email">{{ currentUser.email }}</span>
+        </div>
+        <div class="metadata-row-item">
+          <span class="meta-label">Node Status:</span>
+          <span class="meta-value-status">{{ currentUser.accountStatus }}</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="profile-details-ledger-panel">
+
+      <div class="ledger-header-box">
+        <h1 class="ledger-main-title">Account Parameters Matrix</h1>
+        <p class="ledger-subtitle">Monitor financial metrics, active statuses and digital asset storage totals.</p>
+      </div>
+
+      <div class="financial-cards-row-grid">
+        <div class="financial-metric-card-box asset-glow-purple">
+          <div class="metric-card-inner" style="position: relative; display: flex; flex-direction: column; height: 100%;">
+            <span class="metric-card-label">
+              {{ getIsDeveloper() ? 'Developer Revenue Pool' : 'Available Wallet Balance' }}
+            </span>
+            <h3 class="metric-card-value text-gradient-neon">
+              {{ currentUser.walletBalance | currency:'USD':'symbol':'1.2-2' }}
+            </h3>
+            <p class="metric-card-desc" style="margin-bottom: auto; padding-bottom: 12px;">
+              {{ getIsDeveloper() ? 'Cleared funds ready for payout.' : 'Stored credit tokens assigned to checkout purchases.' }}
+            </p>
+
+            <button (click)="showTopUpForm = !showTopUpForm"
+                    type="button"
+                    style="background: rgba(126, 91, 216, 0.15); border: 1px solid #7E5BD8; border-radius: 0.375rem; padding: 8px 16px; color: #00d2ff; font-weight: 700; font-size: 0.8rem; cursor: pointer; text-transform: uppercase; letter-spacing: 0.05em; transition: all 250ms ease; display: inline-flex; align-items: center; justify-content: center; width: fit-content;"
+                    onmouseover="this.style.background='#7E5BD8'; this.style.color='#ffffff'; this.style.boxShadow='0 0 12px rgba(126, 91, 216, 0.5)'"
+                    onmouseout="this.style.background='rgba(126, 91, 216, 0.15)'; this.style.color='#00d2ff'; this.style.boxShadow='none'">
+              {{ showTopUpForm ? '✖ Cancel Top Up' : '⚡ Top Up Balance' }}
+            </button>
+          </div>
+        </div>
+
+        <div class="financial-metric-card-box asset-glow-cyan">
+          <div class="metric-card-inner">
+            <span class="metric-card-label">
+              {{ getIsDeveloper() ? 'Global Platform Projects' : 'Total Investment Value' }}
+            </span>
+            <h3 class="metric-card-value text-gradient-cyan">
+              @if (getIsDeveloper()) {
+                {{ myGames.length }} Games
+              } @else {
+                {{ currentUser.totalSpent | currency:'USD':'symbol':'1.2-2' }}
+              }
+            </h3>
+            <p class="metric-card-desc">
+              {{ getIsDeveloper() ? 'Total game titles published inside the Nexus Store ecosystem.' : 'Cumulative historical monetary spending across checkout nodes.' }}
+            </p>
+          </div>
+        </div>
+      </div>
+
+      <div class="ecosystem-analytics-panel-card" style="margin-bottom: 24px; animation: fadeIn 300ms ease;" *ngIf="showTopUpForm">
+        <h4 class="analytics-card-title" style="color: #00d2ff; margin-bottom: 6px;">Credit Terminal Injection Node</h4>
+        <p class="ledger-subtitle" style="margin-bottom: 16px; font-size: 0.8rem;">Deposit digital monetary tokens directly into your encrypted system wallet pool.</p>
+
+        <div *ngIf="fundSuccessMessage" class="alert-banner alert-success" style="padding: 10px 14px; background: rgba(16, 185, 129, 0.1); border: 1px solid #10b981; color: #10b981; border-radius: 6px; font-size: 0.85rem; margin-bottom: 14px;">
+          {{ fundSuccessMessage }}
+        </div>
+
+        <form (ngSubmit)="onDepositFunds()" #fundsForm="ngForm" style="display: flex; gap: 12px; align-items: flex-end; width: 100%;">
+          <div style="display: flex; flex-direction: column; gap: 6px; flex-grow: 1;">
+            <label style="font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: #a0a0b8; font-weight: 700;">Transaction Amount (USD)</label>
+            <input type="number"
+                   name="amount"
+                   min="1"
+                   max="10000"
+                   required
+                   [(ngModel)]="topUpAmount"
+                   placeholder="Enter amount (e.g. 25.00)"
+                   style="background: rgba(31, 31, 46, 0.5); border: 1px solid #1f1f2e; border-radius: 0.5rem; padding: 12px 16px; color: #ffffff; font-family: inherit; font-size: 0.9rem; outline: none; width: 100%; transition: border-color 200ms ease;"
+                   onfocus="this.style.borderColor='#7E5BD8'"
+                   onblur="this.style.borderColor='#1f1f2e'">
+          </div>
+          <button type="submit"
+                  [disabled]="!topUpAmount || topUpAmount <= 0 || isProcessingFunds"
+                  style="background: linear-gradient(90deg, #7E5BD8 0%, #00d2ff 100%); border: none; border-radius: 0.5rem; padding: 12px 24px; color: #ffffff; font-weight: 700; font-size: 0.9rem; cursor: pointer; white-space: nowrap; transition: opacity 200ms ease; height: 46px;">
+            {{ isProcessingFunds ? 'Authorizing Transfer...' : 'Inject Funds' }}
+          </button>
+        </form>
+      </div>
+
+      <div class="ecosystem-analytics-panel-card" style="margin-bottom: 24px;">
+        <h4 class="analytics-card-title">Ecosystem Asset Registry Summary</h4>
+
+        <div class="analytics-stats-split-row">
+          <div class="analytics-stat-node">
+            <span class="stat-node-counter">
+              {{ getIsDeveloper() ? myGames.length : myPurchasedGames.length }}
+            </span>
+            <span class="stat-node-description">
+              {{ getIsDeveloper() ? 'Published Titles' : 'Owned Digital Core Licenses' }}
+            </span>
+          </div>
+
+          <div class="analytics-vertical-divider"></div>
+
+          <div class="analytics-stat-node">
+            <span class="stat-node-counter">
+              {{ currentUser.library?.totalPlaytimeSum || 0 }}h
+            </span>
+            <span class="stat-node-description">
+              {{ getIsDeveloper() ? 'Global Testing Runtime' : 'Accumulated Simulation Runtime' }}
+            </span>
+          </div>
+        </div>
+      </div>
+
+      <div class="ecosystem-analytics-panel-card" *ngIf="getIsDeveloper()">
+        <h4 class="analytics-card-title" style="color: #a27cff; margin-bottom: 12px;">💻 Developer Operations Terminal</h4>
+        <p class="ledger-subtitle" style="margin-bottom: 20px; font-size: 0.85rem;">Manage studio publishing framework parameters.</p>
+
+        <div style="display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 24px;">
+          <button (click)="toggleCreateGame()" type="button" class="dev-action-btn" [class.active]="showCreateGameForm">➕ Create Game</button>
+          <button (click)="toggleEditGame()" type="button" class="dev-action-btn" [class.active]="showEditGameForm">✏️ Edit Game</button>
+          <button (click)="toggleDeleteGame()" type="button" class="dev-action-btn-danger" [class.active]="showDeleteGameForm">🗑️ Remove Game</button>
+          <button (click)="toggleReports()" type="button" class="dev-action-btn" [class.active]="showReportsView">📊 View Reports</button>
+        </div>
+
+        <div class="dev-sub-panel" *ngIf="showCreateGameForm">
+          <h5 style="color: #ffffff; margin-bottom: 8px; font-size: 1.1rem;">Publish New Game Title</h5>
+          <form (ngSubmit)="onCreateGameSubmit()" #createForm="ngForm" style="display: flex; flex-direction: column; gap: 12px;">
+            <input type="text" placeholder="Game Title" class="dev-form-input" name="title" [(ngModel)]="newGame.title" required />
+            <textarea placeholder="Game Description" class="dev-form-input" style="min-height: 80px;" name="desc" [(ngModel)]="newGame.description" required></textarea>
+            <input type="number" placeholder="Price (USD)" class="dev-form-input" name="price" [(ngModel)]="newGame.price" required />
+            <input type="text" placeholder="Version (e.g. 1.0.0)" class="dev-form-input" name="version" [(ngModel)]="newGame.version" />
+            <input type="text" placeholder="Tags (comma separated)" class="dev-form-input" name="tags" [(ngModel)]="newGame.tagsList" />
+            <div style="display:flex; flex-direction: column; gap: 4px;">
+              <label style="font-size:0.75rem; color:#a0a0b8;">Game Cover Art / Banner</label>
+              <input type="file" (change)="onFileSelected($event)" class="dev-form-input" style="padding: 8px;" />
+            </div>
+            <button type="submit" [disabled]="createForm.invalid" class="dev-submit-btn">Publish to Store</button>
+          </form>
+        </div>
+
+        <div class="dev-sub-panel" *ngIf="showEditGameForm">
+          <h5 style="color: #ffffff; margin-bottom: 8px; font-size: 1.1rem;">Update Game Parameters</h5>
+          <select class="dev-form-input" (change)="onSelectGameForEdit($any($event.target).value)" style="margin-bottom: 16px;">
+            <option value="" disabled selected>-- Select a game to alter --</option>
+            <option *ngFor="let gm of myGames" [value]="gm.gameId">{{ gm.title }} (ID: {{gm.gameId}})</option>
+          </select>
+          <form *ngIf="editingGame" (ngSubmit)="onEditGameSubmit()" style="display: flex; flex-direction: column; gap: 12px;">
+            <input type="text" placeholder="New Title" class="dev-form-input" name="etitle" [(ngModel)]="editingGame.title" required />
+            <textarea placeholder="New Description" class="dev-form-input" name="edesc" [(ngModel)]="editingGame.description" style="min-height: 80px;" required></textarea>
+            <input type="number" placeholder="New Price (USD)" class="dev-form-input" name="eprice" [(ngModel)]="editingGame.price" required />
+            <input type="text" placeholder="Version" class="dev-form-input" name="eversion" [(ngModel)]="editingGame.version" />
+            <input type="text" placeholder="Tags List" class="dev-form-input" name="etags" [(ngModel)]="editingGame.tagsList" />
+            <div style="display:flex; flex-direction: column; gap: 4px;">
+              <label style="font-size:0.75rem; color:#a0a0b8;">Replace Cover Art (Optional)</label>
+              <input type="file" (change)="onFileSelected($event)" class="dev-form-input" style="padding: 8px;" />
+            </div>
+            <button type="submit" class="dev-submit-btn">Apply Record Changes</button>
+          </form>
+        </div>
+
+        <div class="dev-sub-panel" *ngIf="showDeleteGameForm">
+          <h5 style="color: #ef4444; margin-bottom: 8px; font-size: 1.1rem;">Purge Asset from Store</h5>
+          <p style="font-size: 0.8rem; color: #a0a0b8; margin-bottom: 12px;">Warning: This action is permanent and will drop the game from global storage.</p>
+          <form (ngSubmit)="onDeleteGameSubmit()" style="display: flex; flex-direction: column; gap: 12px;">
+            <select class="dev-form-input" name="dropId" [(ngModel)]="gameToBurnId" required>
+              <option [ngValue]="null" disabled selected>-- Select target game to delete --</option>
+              <option *ngFor="let gm of myGames" [ngValue]="gm.gameId">{{ gm.title }} (ID: {{gm.gameId}})</option>
+            </select>
+            <button type="submit" [disabled]="!gameToBurnId" class="dev-submit-btn-danger">Permalock & Delete</button>
+          </form>
+        </div>
+
+        <div class="dev-sub-panel" *ngIf="showReportsView">
+          <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap; gap: 12px;">
+            <h5 style="color: #00d2ff; margin: 0; font-size: 1.1rem;">Bug Reports & Feedback Matrix</h5>
+
+            <select class="dev-form-input" name="reportGameFilter" [(ngModel)]="selectedReportGameId" style="max-width: 250px; padding: 6px 12px; font-size: 0.85rem; height: auto;">
+              <option [ngValue]="null">-- All Published Games --</option>
+              <option *ngFor="let gm of myGames" [value]="gm.gameId">{{ gm.title }}</option>
+            </select>
+          </div>
+
+          <div *ngIf="getFilteredReports().length === 0" style="padding: 24px; text-align: center; color: #7c7c94; font-size: 0.9rem; background: #0f0f16; border-radius: 0.5rem; border: 1px dashed #2a2a40;">
+            No error logs or testing reports filed under the selected product matrix scope.
+          </div>
+
+          <div style="display: flex; flex-direction: column; gap: 14px;">
+            <div *ngFor="let rep of getFilteredReports()"
+                 style="background: #0f0f16; border-radius: 0.5rem; padding: 18px; border: 1px solid #2a2a40; display: flex; flex-direction: column; gap: 10px; transition: border-color 200ms ease; border-left-width: 4px;"
+                 [style.borderLeftColor]="rep.status === 'RESOLVED' ? '#10b981' : (rep.status === 'IN_PROGRESS' ? '#f59e0b' : '#ef4444')">
+
+              <div style="display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; width: 100%;">
+                <div>
+                  <h4 style="font-size: 1rem; color: #ffffff; margin: 0 0 4px 0; font-family: 'Rajdhani', sans-serif; font-weight: 700;">
+                    {{ rep.title }} <span style="color: #7E5BD8; font-size: 0.85rem; font-weight: 500;">&#64; {{ rep.gameTitle }}</span>
+                  </h4>
+                  <p style="font-size: 0.75rem; color: #a0a0b8; margin: 0;">
+                    Report Index: #{{ rep.reportId }} • Reporter Node: User #{{ rep.userId }}
+                    <span *ngIf="rep.contactEmail"> • Contact Channel: {{ rep.contactEmail }}</span>
+                  </p>
+                </div>
+
+                <div style="display: flex; gap: 6px; align-items: center;">
+                  <span style="font-size: 0.65rem; font-weight: 800; padding: 3px 8px; border-radius: 4px; text-transform: uppercase; border: 1px solid;"
+                        [style.backgroundColor]="rep.severityLevel === 'Critical' ? 'rgba(239, 68, 68, 0.15)' : (rep.severityLevel === 'High' ? 'rgba(245, 158, 11, 0.15)' : 'rgba(0, 210, 255, 0.1)')"
+                        [style.color]="rep.severityLevel === 'Critical' ? '#ef4444' : (rep.severityLevel === 'High' ? '#f59e0b' : '#00d2ff')"
+                        [style.borderColor]="rep.severityLevel === 'Critical' ? '#ef4444' : (rep.severityLevel === 'High' ? '#f59e0b' : '#00d2ff')">
+                    {{ rep.severityLevel }}
+                  </span>
+
+                  <span [ngClass]="{
+                          'badge-customer': rep.status === 'PENDING',
+                          'badge-developer': rep.status === 'IN_PROGRESS'
+                        }"
+                        [style.backgroundColor]="rep.status === 'RESOLVED' ? 'rgba(16, 185, 129, 0.15)' : null"
+                        [style.color]="rep.status === 'RESOLVED' ? '#10b981' : null"
+                        [style.border]="rep.status === 'RESOLVED' ? '1px solid rgba(16, 185, 129, 0.3)' : null"
+                        style="font-size: 0.65rem; font-weight: 700; padding: 4px 10px; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.05em;">
+                    {{ rep.status }}
+                  </span>
+                </div>
+              </div>
+
+              <p style="font-size: 0.85rem; color: #e2e2e9; margin: 4px 0; background: rgba(0,0,0,0.25); padding: 12px; border-radius: 6px; line-height: 1.45; border: 1px solid #1f1f2e;">
+                {{ rep.bodyText }}
+              </p>
+
+              <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px;">
+                <span style="font-size: 0.75rem; color: #7c7c94;">
+                  🔄 Reproducible: <strong [style.color]="rep.reproducible === 'yes' ? '#ef4444' : '#a0a0b8'">{{ rep.reproducible | uppercase }}</strong>
+                </span>
+
+                <div style="display: flex; gap: 8px;">
+                  <button *ngIf="rep.status !== 'IN_PROGRESS' && rep.status !== 'RESOLVED'"
+                          (click)="onAlterReportStatus(rep, 'IN_PROGRESS')"
+                          type="button"
+                          style="background: rgba(245, 158, 11, 0.1); border: 1px solid #f59e0b; color: #f59e0b; padding: 5px 12px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; cursor: pointer; transition: all 150ms ease;"
+                          onmouseover="this.style.background='#f59e0b'; this.style.color='#ffffff'"
+                          onmouseout="this.style.background='rgba(245, 158, 11, 0.1)'; this.style.color='#f59e0b'">
+                    ⚙️ Fix in Progress
+                  </button>
+
+                  <button *ngIf="rep.status !== 'RESOLVED'"
+                          (click)="onAlterReportStatus(rep, 'RESOLVED')"
+                          type="button"
+                          style="background: rgba(16, 185, 129, 0.1); border: 1px solid #10b981; color: #10b981; padding: 5px 12px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; cursor: pointer; transition: all 150ms ease;"
+                          onmouseover="this.style.background='#10b981'; this.style.color='#ffffff'"
+                          onmouseout="this.style.background='rgba(16, 185, 129, 0.1)'; this.style.color='#10b981'">
+                    ✅ Resolve Ticket
+                  </button>
+                </div>
+              </div>
+
+            </div>
+          </div>
+        </div>
+
+      </div>
+    </div>
+  </div>
+</div>

+ 78 - 0
final project/web-game-shop/src/app/components/profile/profile.spec.ts

@@ -0,0 +1,78 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { CommonModule, CurrencyPipe, DatePipe } from '@angular/common';
+import { RouterLink, Router } from '@angular/router';
+import { User } from '../../models/user.model';
+import { UserService } from '../../services/user/user';
+
+@Component({
+  selector: 'app-profile',
+  standalone: true,
+  imports: [CommonModule, RouterLink, CurrencyPipe],
+  templateUrl: './profile.html',
+  styleUrls: ['./profile.css']
+})
+export class ProfileComponent implements OnInit {
+  currentUser: User | null = null;
+  errorMessage: string | null = null;
+  isLoading: boolean = true;
+
+  // 2. Injected ChangeDetectorRef inside the component constructor setup
+  constructor(
+    private userService: UserService,
+    private router: Router,
+    private cdr: ChangeDetectorRef
+  ) {}
+
+  ngOnInit(): void {
+    const sessionData = localStorage.getItem('nexus_mock_session');
+
+    if (sessionData) {
+      try {
+        const parsedSession = JSON.parse(sessionData);
+        const authenticatedId = parsedSession.userId;
+
+        if (authenticatedId) {
+          this.loadUserProfileFromServer(authenticatedId);
+        } else {
+          this.handleInvalidSession("Session reference token missing identifier mapping.");
+        }
+      } catch (error) {
+        this.handleInvalidSession("Failed to parse structural operational session sequences.");
+      }
+    } else {
+      this.handleInvalidSession("No active user session verified on this node.");
+    }
+  }
+
+  private loadUserProfileFromServer(userId: number): void {
+    this.isLoading = true;
+    this.userService.getUserById(userId).subscribe({
+      next: (userData: User) => {
+        this.currentUser = userData;
+        this.isLoading = false;
+
+        // 3. CRITICAL FIX: Explicitly notify Angular layout compilation engine to repaint the view now
+        this.cdr.detectChanges();
+      },
+      error: (serverError) => {
+        this.errorMessage = "Failed to communicate with remote database matrix layer.";
+        this.isLoading = false;
+
+        // 4. Force state update on error mapping branches as well
+        this.cdr.detectChanges();
+        console.error("Profile payload sync abort trace:", serverError);
+      }
+    });
+  }
+
+  private handleInvalidSession(logMessage: string): void {
+    console.warn(logMessage);
+    this.isLoading = false;
+    this.router.navigate(['/login']);
+    this.cdr.detectChanges(); // Sync view state changes
+  }
+
+  getIsDeveloper(): boolean {
+    return this.currentUser?.role?.toUpperCase() === 'DEVELOPER';
+  }
+}

+ 361 - 0
final project/web-game-shop/src/app/components/profile/profile.ts

@@ -0,0 +1,361 @@
+import {Component, OnInit, ChangeDetectorRef, NgZone} from '@angular/core';
+import {CommonModule, CurrencyPipe} from '@angular/common';
+import {RouterLink, Router, ActivatedRoute} from '@angular/router';
+import {FormsModule} from '@angular/forms';
+import {User} from '../../models/user.model';
+import {Game} from '../../models/game.model';
+import {Report} from '../../models/report';
+import {UserService} from '../../services/user/user';
+import {GameService} from '../../services/game/game';
+import {LibraryService} from '../../services/library/library';
+
+@Component({
+  selector: 'app-profile',
+  standalone: true,
+  imports: [CommonModule, CurrencyPipe, FormsModule],
+  templateUrl: './profile.html',
+  styleUrls: ['./profile.css']
+})
+export class ProfileComponent implements OnInit {
+  currentUser: User | null = null;
+  errorMessage: string | null = null;
+  isLoading: boolean = true;
+
+  myGames: Game[] = [];
+  myReports: Report[] = [];
+  myPurchasedGames: Game[] = [];
+  selectedReportGameId: number | null = null;
+
+  newGame: Game = {title: '', description: '', price: 0, version: '1.0', tagsList: ''};
+  editingGame: Game | null = null;
+  gameToBurnId: number | null = null;
+
+  selectedFile: File | null = null;
+
+  topUpAmount: number | null = null;
+  isProcessingFunds: boolean = false;
+  fundSuccessMessage: string | null = null;
+  showTopUpForm: boolean = false;
+
+  showCreateGameForm: boolean = false;
+  showEditGameForm: boolean = false;
+  showDeleteGameForm: boolean = false;
+  showReportsView: boolean = false;
+
+  constructor(
+    private userService: UserService,
+    private gameService: GameService,
+    private libraryService: LibraryService,
+    private router: Router,
+    private route: ActivatedRoute,
+    private cdr: ChangeDetectorRef,
+    private zone: NgZone
+  ) {
+  }
+
+  ngOnInit(): void {
+    const sessionData = localStorage.getItem('nexus_mock_session');
+    if (sessionData) {
+      try {
+        const parsedSession = JSON.parse(sessionData);
+        const authenticatedId = parsedSession.userId;
+        if (authenticatedId) {
+          // Only load the profile first
+          this.loadUserProfileFromServer(authenticatedId);
+          this.interceptPaymentRouteStatus(authenticatedId);
+        } else {
+          this.handleInvalidSession("Session reference token missing identifier mapping.");
+        }
+      } catch (error) {
+        this.handleInvalidSession("Failed to parse structural operational session sequences.");
+      }
+    } else {
+      this.handleInvalidSession("No active user session verified on this node.");
+    }
+  }
+
+  private loadUserProfileFromServer(userId: number): void {
+    this.isLoading = true;
+    this.userService.getUserById(userId).subscribe({
+      next: (userData: any) => {
+        this.zone.run(() => {
+          this.currentUser = userData;
+          this.isLoading = false;
+
+          if (this.getIsDeveloper()) {
+            this.loadDeveloperGames();
+          } else {
+            // Завантажуємо реальні елементи бібліотеки з сервера
+            this.libraryService.getUserLibrary(userId).subscribe({
+              next: (libraryItems: any[]) => {
+                this.myPurchasedGames = libraryItems;
+                this.cdr.detectChanges();
+              },
+              error: (err) => {
+                console.error("Failed to load profile library items", err);
+                this.myPurchasedGames = [];
+                this.cdr.detectChanges();
+              }
+            });
+          }
+
+          this.cdr.detectChanges();
+        });
+      },
+      error: (serverError) => {
+        this.zone.run(() => {
+          this.errorMessage = "Failed to communicate with remote database matrix layer.";
+          this.isLoading = false;
+          this.cdr.detectChanges();
+        });
+        console.error("Profile payload sync abort trace:", serverError);
+      }
+    });
+  }
+
+  private loadDeveloperGames(): void {
+    const currentDevId = this.currentUser?.userId;
+    // Safety guard. If there is no developer ID, we can't fetch anything anyway.
+    if (!currentDevId) return;
+
+    this.gameService.getGames().subscribe({
+      next: (allGames: Game[]) => {
+        // Filter out games that belong to this specific logged-in developer
+        this.myGames = allGames.filter(game =>
+          game.developer && game.developer.developerId === currentDevId
+        );
+
+        this.loadAllDeveloperReports();
+        this.cdr.detectChanges();
+      },
+      error: (err) => console.error("Could not load games", err)
+    });
+  }
+
+  private loadAllDeveloperReports(): void {
+    this.myReports = [];
+    if (!this.myGames || this.myGames.length === 0) return;
+
+    this.myGames.forEach(game => {
+      if (game.gameId) {
+        this.gameService.getReportsByGame(game.gameId).subscribe({
+          next: (reports: any[]) => {
+            const enriched: Report[] = reports.map(r => ({
+              reportId: r.reportId,
+              title: r.title,
+              bodyText: r.bodyText,
+              severityLevel: r.severity || r.severityLevel || r.bugSeverity || 'Medium',
+              reproducible: r.reproducible,
+              contactEmail: r.contactEmail,
+              status: r.status || r.reportStatus || 'PENDING',
+              createdAt: r.createdAt,
+              gameId: game.gameId!,
+              gameTitle: game.title,
+              userId: r.user?.userId || 0
+            }));
+
+            this.myReports = [...this.myReports, ...enriched];
+            this.cdr.detectChanges();
+          },
+          error: (err) => console.warn(`Telemetry polling skipped for index context node: ${game.gameId}`, err)
+        });
+      }
+    });
+  }
+
+  onAlterReportStatus(report: Report, newStatus: 'PENDING' | 'IN_PROGRESS' | 'RESOLVED'): void {
+    this.gameService.updateReportStatus(report.gameId, report.reportId, newStatus).subscribe({
+      next: (updated: any) => {
+        report.status = updated.status || newStatus;
+        alert(`Ticket status altered successfully to: ${newStatus}`);
+        this.cdr.detectChanges();
+      },
+      error: (err) => console.error("Failed to commit status change transaction boundary:", err)
+    });
+  }
+
+  getFilteredReports(): Report[] {
+    // If select is set to "All", just return the whole bunch
+    if (!this.selectedReportGameId) {
+      return this.myReports;
+    }
+    // Use '+' unary operator to cast the string from value bind back to a number
+    return this.myReports.filter(r => r.gameId === +this.selectedReportGameId!);
+  }
+
+  onFileSelected(event: any): void {
+    const file: File = event.target.files[0];
+    if (file) {
+      this.selectedFile = file;
+    }
+  }
+
+  onCreateGameSubmit(): void {
+    if (!this.currentUser) return;
+    const formData = new FormData();
+    this.newGame.developer = {developerId: this.currentUser.userId || 1};
+    formData.append('game', JSON.stringify(this.newGame));
+    if (this.selectedFile) {
+      formData.append('image', this.selectedFile);
+    }
+    this.gameService.createGame(formData).subscribe({
+      next: () => {
+        alert('Game published successfully!');
+        this.showCreateGameForm = false;
+        this.loadDeveloperGames();
+      },
+      error: (err) => console.error('Error creating game', err)
+    });
+  }
+
+  onSelectGameForEdit(gameId: number): void {
+    const found = this.myGames.find(g => g.gameId === +gameId);
+    this.editingGame = found ? {...found} : null;
+  }
+
+  onEditGameSubmit(): void {
+    if (!this.editingGame || !this.editingGame.gameId || !this.currentUser) return;
+
+    const formData = new FormData();
+    const gameToSave = {
+      ...this.editingGame,
+      developer: {
+        developerId: this.currentUser.userId
+      }
+    };
+
+    formData.append('game', JSON.stringify(gameToSave));
+
+    if (this.selectedFile) {
+      formData.append('image', this.selectedFile);
+    }
+
+    this.gameService.updateGame(this.editingGame.gameId, formData).subscribe({
+      next: () => {
+        alert('Game updated successfully!');
+        this.showEditGameForm = false;
+        this.editingGame = null;
+        this.selectedFile = null;
+        this.loadDeveloperGames();
+      },
+      error: (err) => console.error('Error updating game', err)
+    });
+  }
+
+  onDeleteGameSubmit(): void {
+    if (!this.gameToBurnId) return;
+    if (confirm('Permalock and drop this asset from global store?')) {
+      this.gameService.deleteGame(this.gameToBurnId).subscribe({
+        next: () => {
+          alert('Game purged from store listings.');
+          this.showDeleteGameForm = false;
+          this.gameToBurnId = null;
+          this.loadDeveloperGames();
+        },
+        error: () => {
+          this.showDeleteGameForm = false;
+          this.loadDeveloperGames();
+        }
+      });
+    }
+  }
+
+  private interceptPaymentRouteStatus(userId: number): void {
+    this.route.queryParams.subscribe(params => {
+      const status = params['status'];
+      if (status === 'success') {
+        const verifiedAmountStr = localStorage.getItem('nexus_pending_fund_amount');
+        if (verifiedAmountStr) {
+          const amount = parseFloat(verifiedAmountStr);
+          localStorage.removeItem('nexus_pending_fund_amount');
+          this.isProcessingFunds = true;
+          this.userService.simulateWebhookPayment(userId, amount).subscribe({
+            next: () => {
+              this.zone.run(() => {
+                this.loadUserProfileFromServer(userId);
+                this.fundSuccessMessage = `Stripe Gateway Confirmed! Added +$${amount.toFixed(2)}.`;
+                this.showTopUpForm = false;
+                this.isProcessingFunds = false;
+                this.cdr.detectChanges();
+              });
+              this.router.navigate([], {queryParams: {status: null}, queryParamsHandling: 'merge'});
+            },
+            error: () => {
+              this.zone.run(() => {
+                this.isProcessingFunds = false;
+                this.errorMessage = "Failed to synchronize Stripe ledger.";
+                this.cdr.detectChanges();
+              });
+            }
+          });
+        }
+      } else if (status === 'cancel') {
+        this.errorMessage = "Transaction session execution terminated by user.";
+        this.router.navigate([], {queryParams: {status: null}, queryParamsHandling: 'merge'});
+      }
+    });
+  }
+
+  onDepositFunds(): void {
+    if (!this.currentUser || !this.currentUser.userId || !this.topUpAmount || this.topUpAmount <= 0) return;
+    this.isProcessingFunds = true;
+    localStorage.setItem('nexus_pending_fund_amount', this.topUpAmount.toString());
+
+    this.userService.createStripeSession(this.currentUser.userId, this.topUpAmount).subscribe({
+      next: (res: { url: string }) => {
+        window.location.href = res.url;
+      },
+      error: () => {
+        this.zone.run(() => {
+          this.isProcessingFunds = false;
+          this.errorMessage = "Stripe subsystem is currently unreachable.";
+          this.cdr.detectChanges();
+        });
+      }
+    });
+  }
+
+  private handleInvalidSession(logMessage: string): void {
+    console.warn(logMessage);
+    this.zone.run(() => {
+      this.isLoading = false;
+      this.router.navigate(['/login']);
+      this.cdr.detectChanges();
+    });
+  }
+
+  toggleCreateGame(): void {
+    this.showCreateGameForm = !this.showCreateGameForm;
+    this.showEditGameForm = false;
+    this.showDeleteGameForm = false;
+    this.showReportsView = false;
+  }
+
+  toggleEditGame(): void {
+    this.showEditGameForm = !this.showEditGameForm;
+    this.showCreateGameForm = false;
+    this.showDeleteGameForm = false;
+    this.showReportsView = false;
+  }
+
+  toggleDeleteGame(): void {
+    this.showDeleteGameForm = !this.showDeleteGameForm;
+    this.showCreateGameForm = false;
+    this.showEditGameForm = false;
+    this.showReportsView = false;
+  }
+
+  toggleReports(): void {
+    this.showReportsView = !this.showReportsView;
+    this.showCreateGameForm = false;
+    this.showEditGameForm = false;
+    this.showDeleteGameForm = false;
+    if (this.showReportsView) {
+      this.loadAllDeveloperReports();
+    }
+  }
+
+  protected getIsDeveloper(): boolean {
+    return this.currentUser?.role?.toUpperCase() === 'DEVELOPER';
+  }
+}

+ 338 - 0
final project/web-game-shop/src/app/components/register/register.css

@@ -0,0 +1,338 @@
+/* Container root structural coordinates mapping across deep space canvas layout views */
+.auth-page-container {
+  min-height: 100vh;
+  width: 100%;
+  background-color: #0b0b11;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+  font-family: 'Inter', sans-serif;
+  overflow-x: hidden;
+}
+
+.auth-split-matrix-layout {
+  display: flex;
+  width: 100%;
+  min-height: 100vh;
+}
+
+/* LEFT SIDEBAR CORE PRESENTATION MAPPINGS STYLE SHEETS */
+.auth-left-brand-panel {
+  flex: 1.2;
+  background-image:
+    linear-gradient(135deg, rgba(15, 15, 22, 0.85) 0%, rgba(11, 11, 17, 0.92) 100%),
+    url('../../assets/background-register-login.png');
+  background-size: cover;
+  background-position: center center;
+  background-repeat: no-repeat;
+
+  padding: 60px 80px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  box-sizing: border-box;
+  position: relative;
+  overflow: hidden;
+}
+
+.auth-left-brand-panel::before {
+  content: '';
+  position: absolute;
+  top: -10%;
+  left: -10%;
+  width: 300px;
+  height: 300px;
+  background-color: rgba(126, 91, 216, 0.15);
+  border-radius: 50%;
+  filter: blur(80px);
+  pointer-events: none;
+  z-index: 1;
+}
+
+@media (max-width: 960px) {
+  .auth-left-brand-panel { display: none; }
+}
+
+.brand-identity-anchor {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 48px;
+}
+
+.brand-gradient-logo-box {
+  width: 40px;
+  height: 40px;
+  border-radius: 0.5rem;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-image: linear-gradient(to bottom right, #7E5BD8, #00d2ff);
+  color: #ffffff;
+}
+
+.logo-svg-vector {
+  width: 30px;
+  height: 30px;
+}
+
+.brand-name-text {
+  font-family: 'Rajdhani', sans-serif;
+  font-weight: 700;
+  font-size: 1.75rem;
+  letter-spacing: 0.1em;
+  color: #ffffff;
+}
+
+.heading-text-white {
+  font-family: 'Rajdhani', sans-serif;
+  font-size: 3rem;
+  font-weight: 700;
+  line-height: 1.1;
+  color: #ffffff;
+  margin: 0 0 16px 0;
+}
+
+.gradient-accent-text {
+  background-image: linear-gradient(to right, #7E5BD8, #00ff88);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.paragraph-text-nexus-muted {
+  font-size: 1rem;
+  line-height: 1.6;
+  color: #a0a0b8;
+  max-width: 460px;
+  margin: 0 0 40px 0;
+}
+
+.feature-bullets-stack-group {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.bullet-row-item {
+  display: flex;
+  align-items: center;
+  gap: 14px;
+}
+
+.checkmark-icon-capsule {
+  width: 22px;
+  height: 22px;
+  border-radius: 50%;
+  background-color: rgba(0, 255, 136, 0.1);
+  color: #00ff88;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 12px;
+  font-weight: 700;
+}
+
+.bullet-label-span {
+  font-size: 0.95rem;
+  color: #e2e2e9;
+}
+
+/* RIGHT SIDEBAR FORM CREDENTIALS SUBSECTION CONFIGURATIONS */
+.auth-right-credentials-panel {
+  flex: 1;
+  display: flex;
+  margin-top: 50px;
+  align-items: center;
+  justify-content: center;
+  padding: 40px;
+  box-sizing: border-box;
+  background-color: #14141e;
+}
+
+.credentials-inner-capsule {
+  width: 100%;
+  max-width: 420px;
+}
+
+.auth-section-header-box {
+  margin-bottom: 32px;
+}
+
+.auth-main-title {
+  font-size: 1.85rem;
+  font-weight: 700;
+  color: #ffffff;
+  margin: 0 0 8px 0;
+}
+
+.auth-subtitle {
+  font-size: 0.9rem;
+  color: #a0a0b8;
+  margin: 0;
+}
+
+/* INPUT ELEMENT STACK CONSTRAINTS DEFINITIONS */
+.native-credential-form-element {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.input-field-group-stack {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.native-input-label {
+  font-size: 0.825rem;
+  font-weight: 600;
+  color: #e2e2e9;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+}
+
+.native-form-text-input {
+  width: 100%;
+  background-color: #12121a;
+  border: 1px solid #2a2a40;
+  border-radius: 0.5rem;
+  padding: 12px 16px;
+  font-size: 0.95rem;
+  color: #ffffff;
+  outline: none;
+  box-sizing: border-box;
+  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.native-form-text-input:focus {
+  border-color: #7E5BD8;
+  box-shadow: 0 0 0 2px rgba(126, 91, 216, 0.2);
+}
+
+.input-validation-error {
+  border-color: #ef4444 !important;
+}
+
+.validation-tip-msg {
+  font-size: 0.775rem;
+  color: #ef4444;
+}
+
+/* РОЛІ (КАРТКИ ВИБОРУ) */
+.role-selector-group {
+  display: flex;
+  gap: 12px;
+}
+
+.role-option-card {
+  flex: 1;
+  position: relative;
+  background-color: #12121a;
+  border: 1px solid #2a2a40;
+  border-radius: 0.5rem;
+  padding: 16px;
+  cursor: pointer;
+  display: flex;
+  flex-direction: column;
+  transition: all 150ms ease;
+}
+
+.role-option-card.active {
+  border-color: #7E5BD8;
+  background-color: rgba(126, 91, 216, 0.05);
+  box-shadow: 0 0 0 1px #7E5BD8;
+}
+
+.role-radio-input {
+  position: absolute;
+  top: 16px;
+  right: 16px;
+  accent-color: #7E5BD8;
+}
+
+.role-card-content {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  padding-right: 20px;
+}
+
+.role-title {
+  font-size: 1rem;
+  font-weight: 700;
+  color: #ffffff;
+}
+
+.role-desc {
+  font-size: 0.75rem;
+  color: #a0a0b8;
+  line-height: 1.4;
+}
+
+/* GRADIENT ACTION ACTION BUTTON STRUCTURAL PATTERNS */
+.action-submit-btn-gradient {
+  width: 100%;
+  padding: 14px;
+  border: none;
+  border-radius: 0.5rem;
+  background-image: linear-gradient(to right, #7E5BD8, #6366f1);
+  color: #ffffff;
+  font-size: 0.95rem;
+  font-weight: 700;
+  cursor: pointer;
+  box-shadow: 0 4px 20px rgba(126, 91, 216, 0.25);
+  transition: all 150ms ease;
+  margin-top: 10px;
+}
+
+.action-submit-btn-gradient:hover:not(:disabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 6px 24px rgba(126, 91, 216, 0.4);
+}
+
+.action-submit-btn-gradient:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+  transform: none;
+}
+
+/* REDIRECT ANCHOR FOOTER NOTES LINK SYSTEM */
+.auth-footer-redirect-msg {
+  text-align: center;
+  font-size: 0.9rem;
+  color: #a0a0b8;
+  margin-top: 28px;
+}
+
+.redirect-hyperlink-token {
+  color: #00ff88;
+  text-decoration: none;
+  font-weight: 600;
+}
+
+.redirect-hyperlink-token:hover {
+  text-decoration: underline;
+}
+
+/* NOTIFICATION ALERT BANNER WRAPPERS */
+.alert-banner {
+  padding: 12px 16px;
+  border-radius: 0.5rem;
+  font-size: 0.875rem;
+  margin-bottom: 20px;
+  font-weight: 500;
+}
+
+.alert-success {
+  background-color: rgba(0, 255, 136, 0.1);
+  color: #00ff88;
+  border: 1px solid rgba(0, 255, 136, 0.2);
+}
+
+.alert-danger {
+  background-color: rgba(239, 68, 68, 0.1);
+  color: #ef4444;
+  border: 1px solid rgba(239, 68, 68, 0.2);
+}

+ 121 - 0
final project/web-game-shop/src/app/components/register/register.html

@@ -0,0 +1,121 @@
+<div class="auth-page-container">
+  <div class="auth-split-matrix-layout">
+
+    <div class="auth-left-brand-panel">
+      <div class="brand-identity-anchor">
+        <div class="brand-gradient-logo-box">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="logo-svg-vector" fill="currentColor">
+            <path d="M448 64c106 0 192 86 192 192S554 448 448 448l-256 0C86 448 0 362 0 256S86 64 192 64l256 0zM192 176c-13.3 0-24 10.7-24 24l0 32-32 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l32 0 0 32c0 13.3 10.7 24 24 24s24-10.7 24-24l0-32 32 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-32 0 0-32c0-13.3-10.7-24-24-24zm240 96a32 32 0 1 0 0 64 32 32 0 1 0 0-64zm64-96a32 32 0 1 0 0 64 32 32 0 1 0 0-64z"/></svg>
+        </div>
+        <span class="brand-name-text">NEXUS</span>
+      </div>
+
+      <h2 class="heading-text-white">Build Your<br><span class="gradient-accent-text">Legacy Here.</span></h2>
+      <p class="paragraph-text-nexus-muted">Whether you're a player hunting the next epic adventure or a developer shipping your masterpiece, NEXUS is your launchpad.</p>
+
+      <div class="feature-bullets-stack-group">
+        <div class="bullet-row-item">
+          <div class="checkmark-icon-capsule">✓</div>
+          <span class="bullet-label-span">Access 12,000+ games instantly</span>
+        </div>
+        <div class="bullet-row-item">
+          <div class="checkmark-icon-capsule">✓</div>
+          <span class="bullet-label-span">Publish & monetize your games</span>
+        </div>
+        <div class="bullet-row-item">
+          <div class="checkmark-icon-capsule">✓</div>
+          <span class="bullet-label-span">Secure wallet & instant purchases</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="auth-right-credentials-panel">
+      <div class="credentials-inner-capsule">
+
+        <div class="auth-section-header-box">
+          <h1 class="auth-main-title">Create your account</h1>
+          <p class="auth-subtitle">Join the nexus network ecosystem today</p>
+        </div>
+
+        @if (successMessage) {
+          <div class="alert-banner alert-success">{{ successMessage }}</div>
+        }
+        @if (errorMessage) {
+          <div class="alert-banner alert-danger">{{ errorMessage }}</div>
+        }
+
+        <form [formGroup]="registrationForm" (ngSubmit)="onSubmit()" class="native-credential-form-element">
+
+          <div class="input-field-group-stack">
+            <label class="native-input-label">Full Name</label>
+            <input type="text"
+                   formControlName="fullName"
+                   placeholder="Alex Chen"
+                   class="native-form-text-input"
+                   [class.input-validation-error]="registrationForm.get('fullName')?.invalid && registrationForm.get('fullName')?.touched">
+            @if (registrationForm.get('fullName')?.invalid && registrationForm.get('fullName')?.touched) {
+              <span class="validation-tip-msg">Name must contain at least 2 alphabet values.</span>
+            }
+          </div>
+
+          <div class="input-field-group-stack">
+            <label class="native-input-label">Email Address</label>
+            <input type="email"
+                   formControlName="email"
+                   placeholder="name@domain.com"
+                   class="native-form-text-input"
+                   [class.input-validation-error]="registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched">
+            @if (registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched) {
+              <span class="validation-tip-msg">Please assign a valid active electronic address format.</span>
+            }
+          </div>
+
+          <div class="input-field-group-stack">
+            <label class="native-input-label">Password</label>
+            <input type="password"
+                   formControlName="password"
+                   placeholder="••••••••"
+                   class="native-form-text-input"
+                   [class.input-validation-error]="registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched">
+            @if (registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched) {
+              <span class="validation-tip-msg">Password token security parameter demands minimum 8 characters.</span>
+            }
+          </div>
+
+          <div class="input-field-group-stack">
+            <label class="native-input-label">Account Type</label>
+            <div class="role-selector-group">
+              <label class="role-option-card" [class.active]="registrationForm.get('role')?.value === 'USER'">
+                <input type="radio" formControlName="role" value="USER" class="role-radio-input">
+                <div class="role-card-content">
+                  <span class="role-title">Player</span>
+                  <span class="role-desc">Play games, build your library, and explore worlds.</span>
+                </div>
+              </label>
+
+              <label class="role-option-card" [class.active]="registrationForm.get('role')?.value === 'DEVELOPER'">
+                <input type="radio" formControlName="role" value="DEVELOPER" class="role-radio-input">
+                <div class="role-card-content">
+                  <span class="role-title">Developer</span>
+                  <span class="role-desc">Publish, manage, and monetize your own games.</span>
+                </div>
+              </label>
+            </div>
+          </div>
+
+          <button type="submit"
+                  [disabled]="registrationForm.invalid || isSubmitting"
+                  class="action-submit-btn-gradient">
+            {{ isSubmitting ? 'Provisioning Account...' : 'Create Account' }}
+          </button>
+        </form>
+
+        <p class="auth-footer-redirect-msg">
+          Already have an account? <a routerLink="/login" class="redirect-hyperlink-token">Sign In</a>
+        </p>
+
+      </div>
+    </div>
+
+  </div>
+</div>

+ 22 - 0
final project/web-game-shop/src/app/components/register/register.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RegisterComponent } from './register';
+
+describe('Register', () => {
+  let component: RegisterComponent;
+  let fixture: ComponentFixture<RegisterComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [RegisterComponent],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(RegisterComponent);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 75 - 0
final project/web-game-shop/src/app/components/register/register.ts

@@ -0,0 +1,75 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule, FormGroup, FormBuilder, Validators, FormsModule } from '@angular/forms';
+import { RouterModule, Router } from '@angular/router';
+import { UserService } from '../../services/user/user'; // Ensure correct path alignment
+import { User } from '../../models/user.model';
+
+@Component({
+  selector: 'app-register',
+  standalone: true,
+  imports: [CommonModule, ReactiveFormsModule, RouterModule, FormsModule],
+  templateUrl: './register.html',
+  styleUrls: ['./register.css']
+})
+export class RegisterComponent implements OnInit {
+  registrationForm!: FormGroup;
+  isSubmitting: boolean = false;
+  successMessage: string | null = null;
+  errorMessage: string | null = null;
+
+  constructor(
+    private fb: FormBuilder,
+    private router: Router,
+    private userService: UserService // Injecting real user persistence layer service
+  ) {}
+
+  ngOnInit(): void {
+    // start values
+    this.registrationForm = this.fb.group({
+      fullName: ['', [Validators.required, Validators.minLength(2)]],
+      email: ['', [Validators.required, Validators.email]],
+      password: ['', [Validators.required, Validators.minLength(8)]],
+      role: ['USER', Validators.required]
+    });
+  }
+
+  onSubmit(): void {
+    if (this.registrationForm.invalid) {
+      this.errorMessage = 'Please fulfill all fields with valid criteria matching schema constraints.';
+      return;
+    }
+
+    this.isSubmitting = true;
+    this.errorMessage = null;
+    this.successMessage = null;
+
+    const newUserPayload: User = {
+      fullName: this.registrationForm.value.fullName,
+      email: this.registrationForm.value.email,
+      passwordHash: this.registrationForm.value.password,
+      role: this.registrationForm.value.role
+    };
+
+    this.userService.register(newUserPayload).subscribe({
+      next: (savedUser: User) => {
+        this.isSubmitting = false;
+        this.successMessage = 'Account stored inside database successfully! Syncing live profile layout...';
+
+        localStorage.setItem('nexus_mock_session', JSON.stringify({
+          fullName: savedUser.fullName,
+          role: savedUser.role || 'USER'
+        }));
+
+        setTimeout(() => {
+          this.router.navigate(['/storefront']);
+        }, 1500);
+      },
+      error: (err) => {
+        this.isSubmitting = false;
+        console.error('Database insertion transmission protocol rejected:', err);
+        this.errorMessage = 'Server transaction fault: Unable to persist profile credentials mapping.';
+      }
+    });
+  }
+}

+ 482 - 0
final project/web-game-shop/src/app/components/storefront/storefront.css

@@ -0,0 +1,482 @@
+/* ==========================================================================
+   Ecosystem Root Parameters & Design System Variables Mirror
+   ========================================================================== */
+:host {
+  display: block;
+  background-color: #0b0b12;
+  color: #ffffff;
+  font-family: 'Rajdhani', 'Inter', sans-serif;
+  min-height: 100vh;
+}
+
+.storefront-content-section {
+  max-width: 1400px;
+  margin: 0 auto;
+  padding: 48px 32px 16px 32px;
+}
+
+/* ==========================================================================
+   1. Hero Banner Component Layout Definitions
+   ========================================================================== */
+.hero-banner-layout {
+  position: relative;
+  min-height: 520px;
+  display: flex;
+  align-items: center;
+  padding: 0 64px;
+  overflow: hidden;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.hero-image-wrapper {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1;
+}
+
+.hero-background-canvas {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  object-position: center;
+  transform: scale(1.02);
+  filter: brightness(0.4) contrast(1.1);
+}
+
+.hero-gradient-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(180deg, rgba(11, 11, 18, 0.3) 0%, #0b0b12 100%);
+}
+
+.hero-content-bounds {
+  position: relative;
+  z-index: 2;
+  max-width: 680px;
+}
+
+.hero-badge-capsule {
+  display: inline-flex;
+  align-items: center;
+  gap: 8px;
+  background-color: rgba(126, 91, 216, 0.15);
+  border: 1px solid rgba(126, 91, 216, 0.4);
+  padding: 6px 14px;
+  border-radius: 100px;
+  margin-bottom: 24px;
+}
+
+.badge-accent-dot {
+  width: 6px;
+  height: 6px;
+  background-color: #7E5BD8;
+  border-radius: 50%;
+  box-shadow: 0 0 10px #7E5BD8;
+}
+
+.badge-text-node {
+  font-size: 11px;
+  font-weight: 700;
+  letter-spacing: 0.1em;
+  color: #c0aeff;
+}
+
+.hero-display-heading {
+  font-size: 48px;
+  font-weight: 800;
+  letter-spacing: 0.02em;
+  line-height: 1.1;
+  margin: 0 0 16px 0;
+  text-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+}
+
+.hero-subheading-narrative {
+  color: #94a3b8;
+  font-size: 16px;
+  line-height: 1.6;
+  margin: 0 0 32px 0;
+}
+
+/* Search bar control nodes */
+.search-bar-structural-capsule {
+  display: flex;
+  align-items: center;
+  background-color: #161623;
+  border: 1px solid rgba(255, 255, 255, 0.08);
+  border-radius: 12px;
+  padding: 0 18px;
+  height: 54px;
+  max-width: 540px;
+  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.search-bar-structural-capsule:focus-within {
+  border-color: #7E5BD8;
+  box-shadow: 0 0 16px rgba(126, 91, 216, 0.25);
+  background-color: #1a1a2e;
+}
+
+.search-vector-container {
+  color: #64748b;
+  display: flex;
+  align-items: center;
+  margin-right: 14px;
+}
+
+.interactive-search-node {
+  background: none;
+  border: none;
+  color: #ffffff;
+  font-size: 15px;
+  width: 100%;
+  outline: none;
+}
+
+.interactive-search-node::placeholder {
+  color: #475569;
+}
+
+/* ==========================================================================
+   2. Section Structural Typography & Dividers
+   ========================================================================== */
+.section-header-row {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  margin-bottom: 28px;
+}
+
+.section-title-token {
+  font-size: 20px;
+  font-weight: 700;
+  letter-spacing: 0.05em;
+  color: #ffffff;
+  margin: 0;
+  white-space: nowrap;
+}
+
+.accent-title-line {
+  height: 2px;
+  background: linear-gradient(90deg, #7E5BD8 0%, rgba(126, 91, 216, 0) 100%);
+  width: 100%;
+}
+
+/* ==========================================================================
+   3. Special Offers Responsive Layout Engine
+   ========================================================================== */
+.special-offers-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
+  gap: 24px;
+}
+
+.special-card-wrapper {
+  background: linear-gradient(145deg, #161623 0%, #11111c 100%);
+  border: 1px solid rgba(255, 255, 255, 0.04);
+  border-radius: 16px;
+  display: flex;
+  overflow: hidden;
+  height: 180px;
+  transition: transform 0.2s ease, border-color 0.2s ease;
+}
+
+.special-card-wrapper:hover {
+  transform: translateY(-4px);
+  border-color: rgba(126, 91, 216, 0.3);
+}
+
+.card-media-shell {
+  width: 40%;
+  position: relative;
+  background-color: #1f1f33;
+}
+
+.mock-game-cover {
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(135deg, #2e1a47 0%, #161623 100%);
+  display: flex;
+  align-items: flex-end;
+  padding: 12px;
+}
+
+.cover-tag-capsule {
+  background-color: rgba(0, 0, 0, 0.6);
+  backdrop-filter: blur(4px);
+  font-size: 11px;
+  font-weight: 600;
+  padding: 2px 8px;
+  border-radius: 4px;
+  color: #94a3b8;
+}
+
+.discount-badge-token {
+  position: absolute;
+  top: 12px;
+  left: 12px;
+  background-color: #ef4444;
+  color: #ffffff;
+  font-weight: 700;
+  font-size: 13px;
+  padding: 4px 8px;
+  border-radius: 6px;
+  box-shadow: 0 4px 10px rgba(239, 68, 68, 0.3);
+}
+
+.card-metadata-shell {
+  width: 60%;
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.game-item-title {
+  font-size: 18px;
+  font-weight: 700;
+  margin: 0 0 6px 0;
+  color: #ffffff;
+}
+
+.game-item-description {
+  color: #64748b;
+  font-size: 13px;
+  line-height: 1.4;
+  margin: 0;
+}
+
+.price-action-nexus {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 12px;
+}
+
+.price-tag-stack {
+  display: flex;
+  flex-direction: column;
+}
+
+.historical-slashed-price {
+  font-size: 12px;
+  color: #475569;
+  text-decoration: line-through;
+}
+
+.active-numeric-price {
+  font-size: 18px;
+  font-weight: 700;
+  color: #10b981;
+}
+
+.action-trigger-cta {
+  background-color: #7E5BD8;
+  color: #ffffff;
+  border: none;
+  font-weight: 600;
+  font-size: 13px;
+  padding: 8px 16px;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+}
+
+.action-trigger-cta:hover {
+  background-color: #6943c2;
+}
+
+/* ==========================================================================
+   4. Browse By Category Filters Track Matrix
+   ========================================================================== */
+.category-capsules-track {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+  margin-bottom: 12px;
+}
+
+.structural-category-pill {
+  background-color: #161623;
+  color: #94a3b8;
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  padding: 10px 20px;
+  border-radius: 10px;
+  font-weight: 600;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.2s ease-in-out;
+}
+
+.structural-category-pill:hover {
+  background-color: #1f1f33;
+  color: #ffffff;
+  border-color: rgba(255, 255, 255, 0.15);
+}
+
+.active-category-node {
+  background-color: #7E5BD8 !important;
+  color: #ffffff !important;
+  border-color: #7E5BD8 !important;
+  box-shadow: 0 4px 12px rgba(126, 91, 216, 0.3);
+}
+
+/* ==========================================================================
+   5. Standard Structured Games Catalog Layout
+   ========================================================================== */
+.catalog-structural-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 24px;
+}
+
+.standard-game-card {
+  background-color: #161623;
+  border: 1px solid rgba(255, 255, 255, 0.03);
+  border-radius: 14px;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  transition: all 0.2s ease;
+}
+
+.standard-game-card:hover {
+  transform: translateY(-4px);
+  border-color: rgba(255, 255, 255, 0.08);
+  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.3);
+}
+
+.standard-media-frame {
+  height: 160px;
+  position: relative;
+}
+
+.standard-mock-art {
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(180deg, #1a1a2e 0%, #11111c 100%);
+  display: flex;
+  align-items: flex-end;
+  padding: 12px;
+}
+
+.standard-version-stamp {
+  font-size: 11px;
+  color: #475569;
+}
+
+.standard-discount-tag {
+  position: absolute;
+  top: 12px;
+  right: 12px;
+  background-color: #ef4444;
+  color: #ffffff;
+  font-size: 11px;
+  font-weight: 700;
+  padding: 2px 6px;
+  border-radius: 4px;
+}
+
+.standard-details-box {
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+}
+
+.standard-game-title {
+  font-size: 16px;
+  font-weight: 700;
+  margin: 0 0 6px 0;
+  color: #ffffff;
+}
+
+.standard-game-desc {
+  color: #64748b;
+  font-size: 13px;
+  line-height: 1.5;
+  margin: 0 0 14px 0;
+  flex-grow: 1;
+}
+
+.meta-tags-row-capsule {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+  margin-bottom: 16px;
+}
+
+.inline-data-tag {
+  background-color: rgba(255, 255, 255, 0.03);
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  color: #94a3b8;
+  font-size: 11px;
+  padding: 2px 8px;
+  border-radius: 4px;
+}
+
+.standard-footer-price-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-top: 1px solid rgba(255, 255, 255, 0.04);
+  padding-top: 14px;
+}
+
+.standard-price-group {
+  display: flex;
+  align-items: baseline;
+  gap: 8px;
+}
+
+.slashed-old-price {
+  font-size: 12px;
+  color: #475569;
+  text-decoration: line-through;
+}
+
+.current-price-display {
+  font-size: 16px;
+  font-weight: 700;
+  color: #ffffff;
+}
+
+.standard-card-cta {
+  background: none;
+  border: 1px solid rgba(255, 255, 255, 0.1);
+  color: #ffffff;
+  font-size: 12px;
+  font-weight: 600;
+  padding: 6px 12px;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.standard-card-cta:hover {
+  background-color: #ffffff;
+  color: #0b0b12;
+  border-color: #ffffff;
+}
+
+/* ==========================================================================
+   6. Fallback Persistence Alerts Styling
+   ========================================================================== */
+.empty-persistence-alert {
+  background-color: #161623;
+  border: 1px dashed rgba(255, 255, 255, 0.1);
+  border-radius: 12px;
+  padding: 40px;
+  text-align: center;
+}
+
+.alert-text-payload {
+  color: #64748b;
+  font-size: 14px;
+  margin: 0;
+}

+ 132 - 0
final project/web-game-shop/src/app/components/storefront/storefront.html

@@ -0,0 +1,132 @@
+<main class="d6d9beed-ffd6-460b-97f9-7e3c8ac47281">
+
+  <section id="hero-banner" class="hero-banner-layout">
+    <div class="hero-image-wrapper">
+      <img src="https://storage.googleapis.com/uxpilot-auth.appspot.com/5e14d73efb-0c4426f87af16ac1050c.png"
+           alt="Cinematic cyberpunk city landscape" class="hero-background-canvas">
+      <div class="hero-gradient-overlay"></div>
+    </div>
+
+    <div class="hero-content-bounds">
+      <div class="hero-badge-capsule">
+        <span class="badge-accent-dot"></span>
+        <span class="badge-text-node">NEXT-GEN DIGITAL STOREFRONT</span>
+      </div>
+      <h1 class="hero-display-heading">ENTER THE NEXUS OF GAMING</h1>
+      <p class="hero-subheading-narrative">
+        Discover legendary worlds, secure exclusive developer drops, and organize your ultimate digital vault directly from persistent database ledger records.
+      </p>
+
+      <div class="search-bar-structural-capsule">
+        <div class="search-vector-container">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16px" height="16px" fill="currentColor">
+            <path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376C296.3 401.1 253.9 416 208 416 93.1 416 0 322.9 0 208S93.1 0 208 0s208 93.1 208 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/>
+          </svg>
+        </div>
+        <input type="text"
+               [(ngModel)]="searchQuery"
+               (input)="applyFilters()"
+               placeholder="Search games across our live catalog database..."
+               class="interactive-search-node">
+      </div>
+    </div>
+  </section>
+
+  <section class="storefront-content-section" *ngIf="specialOffers.length > 0">
+    <div class="section-header-row">
+      <h2 class="section-title-token">SPECIAL OFFERS</h2>
+      <div class="accent-title-line"></div>
+    </div>
+
+    <div class="special-offers-grid">
+      <div class="special-card-wrapper" *ngFor="let promo of specialOffers" (click)="navigateToGameDetails(promo)" style="cursor: pointer;">
+        <div class="card-media-shell">
+          <ng-container *ngIf="promo.imagePath; else fallbackPromoCover">
+            <img [src]="backendServerUrl + promo.imagePath" alt="{{ promo.title }} cover art" style="width: 100%; height: 100%; object-fit: cover;" />
+          </ng-container>
+          <ng-template #fallbackPromoCover>
+            <div class="mock-game-cover">
+              <span class="cover-tag-capsule">{{ promo.version }}</span>
+            </div>
+          </ng-template>
+
+          <div class="discount-badge-token">-{{ promo.discountPercent }}%</div>
+        </div>
+        <div class="card-metadata-shell">
+          <h3 class="game-item-title">{{ promo.title }}</h3>
+          <p class="game-item-description">{{ promo.description | slice:0:90 }}...</p>
+          <div class="price-action-nexus">
+            <div class="price-tag-stack">
+              <span class="historical-slashed-price">${{ promo.price }}</span>
+              <span class="active-numeric-price">${{ calculateDiscountedPrice(promo.price, promo.discountPercent) }}</span>
+            </div>
+            <button class="action-trigger-cta" (click)="navigateToGameDetails(promo); $event.stopPropagation()">View Details</button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+
+  <section class="storefront-content-section">
+    <div class="section-header-row">
+      <h2 class="section-title-token">BROWSE BY CATEGORY</h2>
+      <div class="accent-title-line"></div>
+    </div>
+
+    <div class="category-capsules-track">
+      <button *ngFor="let cat of categories"
+              (click)="selectCategory(cat)"
+              [class.active-category-node]="selectedCategory === cat"
+              class="structural-category-pill">
+        {{ cat }}
+      </button>
+    </div>
+  </section>
+
+  <section class="storefront-content-section">
+    <div class="section-header-row">
+      <h2 class="section-title-token">LIVE ALL GAMES ({{ displayedGames.length }})</h2>
+      <div class="accent-title-line"></div>
+    </div>
+
+    <div class="empty-persistence-alert" *ngIf="displayedGames.length === 0">
+      <p class="alert-text-payload">No game records mapped to the selected query metrics inside table structures.</p>
+    </div>
+
+    <div class="catalog-structural-grid" *ngIf="displayedGames.length > 0">
+      <div class="standard-game-card" *ngFor="let game of displayedGames" (click)="navigateToGameDetails(game)" style="cursor: pointer;">
+        <div class="standard-media-frame">
+          <ng-container *ngIf="game.imagePath; else fallbackStandardCover">
+            <img [src]="backendServerUrl + game.imagePath" alt="{{ game.title }} art canvas" style="width: 100%; height: 100%; object-fit: cover;" />
+          </ng-container>
+          <ng-template #fallbackStandardCover>
+            <div class="standard-mock-art">
+              <span class="standard-version-stamp">v{{ game.version }}</span>
+            </div>
+          </ng-template>
+
+          <div class="standard-discount-tag" *ngIf="game.discountPercent && game.discountPercent > 0">
+            -{{ game.discountPercent }}%
+          </div>
+        </div>
+        <div class="standard-details-box">
+          <h4 class="standard-game-title">{{ game.title }}</h4>
+          <p class="standard-game-desc">{{ game.description | slice:0:70 }}...</p>
+
+          <div class="meta-tags-row-capsule" *ngIf="game.tagsList">
+            <span class="inline-data-tag" *ngFor="let singleTag of game.tagsList.split(',')">{{ singleTag.trim() }}</span>
+          </div>
+
+          <div class="standard-footer-price-row">
+            <div class="standard-price-group">
+              <span class="slashed-old-price" *ngIf="game.discountPercent && game.discountPercent > 0">${{ game.price }}</span>
+              <span class="current-price-display">${{ calculateDiscountedPrice(game.price, game.discountPercent) }}</span>
+            </div>
+            <button class="standard-card-cta" (click)="navigateToGameDetails(game); $event.stopPropagation()">View Details</button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+
+</main>

+ 22 - 0
final project/web-game-shop/src/app/components/storefront/storefront.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { Storefront } from './storefront';
+
+describe('Storefront', () => {
+  let component: Storefront;
+  let fixture: ComponentFixture<Storefront>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [Storefront],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(Storefront);
+    component = fixture.componentInstance;
+    await fixture.whenStable();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 114 - 0
final project/web-game-shop/src/app/components/storefront/storefront.ts

@@ -0,0 +1,114 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Router, RouterModule, ActivatedRoute } from '@angular/router';
+import { GameService } from '../../services/game/game';
+import { Game } from '../../models/game.model';
+
+@Component({
+  selector: 'app-storefront',
+  standalone: true,
+  imports: [CommonModule, FormsModule, RouterModule],
+  templateUrl: './storefront.html',
+  styleUrls: ['./storefront.css']
+})
+export class StorefrontComponent implements OnInit {
+  allGames: Game[] = [];
+  specialOffers: Game[] = [];
+  categories: string[] = [];
+  displayedGames: Game[] = [];
+
+  searchQuery: string = '';
+  selectedCategory: string = 'All';
+  public backendServerUrl: string;
+
+  constructor(
+    private gameService: GameService,
+    private cdr: ChangeDetectorRef,
+    private router: Router,
+    private route: ActivatedRoute
+  ) {
+    this.backendServerUrl = this.gameService.hostMediaRoot;
+  }
+
+  ngOnInit(): void {
+    this.fetchStorefrontData();
+  }
+
+  fetchStorefrontData(): void {
+    this.gameService.getGames().subscribe({
+      next: (data: any) => {
+        const rawArray = data.content ? data.content : (Array.isArray(data) ? data : [data]);
+        this.allGames = [...rawArray];
+
+        this.extractSpecialOffers();
+        this.extractUniqueCategories();
+
+        this.route.queryParams.subscribe(params => {
+          if (params['search']) {
+            this.searchQuery = params['search'];
+          } else {
+            this.searchQuery = '';
+          }
+          this.applyFilters();
+          this.cdr.detectChanges();
+        });
+      },
+      error: (err) => console.error('Failed to resolve ecosystem data maps from database:', err)
+    });
+  }
+
+  navigateToGameDetails(game: Game): void {
+    const targetId = game.gameId || (game as any).id;
+    if (!targetId) return;
+    localStorage.setItem('nexus_last_viewed_game_id', targetId.toString());
+    this.router.navigate(['/games', targetId]);
+  }
+
+  private extractSpecialOffers(): void {
+    this.specialOffers = this.allGames
+      .filter(game => game.discountPercent && game.discountPercent > 0)
+      .sort((a, b) => (b.discountPercent || 0) - (a.discountPercent || 0))
+      .slice(0, 3);
+  }
+
+  private extractUniqueCategories(): void {
+    const uniqueTags = new Set<string>();
+    this.allGames.forEach(game => {
+      if (game.tagsList) {
+        game.tagsList.split(',')
+          .map(tag => tag.trim())
+          .filter(tag => tag.length > 0)
+          .forEach(tag => uniqueTags.add(tag));
+      }
+    });
+    this.categories = ['All', ...Array.from(uniqueTags)];
+  }
+
+  selectCategory(category: string): void {
+    this.selectedCategory = category;
+    this.applyFilters();
+  }
+
+  applyFilters(): void {
+    this.displayedGames = this.allGames.filter(game => {
+      //Safely handle potential null values from the database using fallback strings
+      const gameTitle = game.title || '';
+      const gameDescription = game.description || '';
+      const query = this.searchQuery || '';
+
+      const matchesSearch = gameTitle.toLowerCase().includes(query.toLowerCase()) ||
+        gameDescription.toLowerCase().includes(query.toLowerCase());
+
+      const matchesCategory = this.selectedCategory === 'All' ||
+        (game.tagsList && game.tagsList.split(',').map(t => t.trim().toLowerCase()).includes(this.selectedCategory.toLowerCase()));
+
+      return matchesSearch && matchesCategory;
+    });
+  }
+
+  calculateDiscountedPrice(price: number, discountPercent: number | undefined): number {
+    if (!discountPercent || discountPercent <= 0) return price;
+    return Number((price * (1 - discountPercent / 100)).toFixed(2));
+  }
+}

+ 17 - 0
final project/web-game-shop/src/app/interfaces/auth.ts

@@ -0,0 +1,17 @@
+import { HttpInterceptorFn } from '@angular/common/http';
+
+export const authInterceptor: HttpInterceptorFn = (req, next) => {
+  const token = localStorage.getItem('nexus_auth_token');
+
+  if (token) {
+    // Clone the request and add the authorization header
+    const cloned = req.clone({
+      setHeaders: {
+        Authorization: `Bearer ${token}`
+      }
+    });
+    return next(cloned);
+  }
+
+  return next(req);
+};

+ 13 - 0
final project/web-game-shop/src/app/models/report.ts

@@ -0,0 +1,13 @@
+export interface Report {
+  reportId: number;
+  title: string;
+  bodyText: string;
+  severityLevel: 'Low' | 'Medium' | 'High' | 'Critical';
+  reproducible: string;
+  contactEmail?: string;
+  status: 'PENDING' | 'IN_PROGRESS' | 'RESOLVED';
+  createdAt?: string;
+  gameId: number;
+  gameTitle?: string;
+  userId: number;
+}

+ 22 - 0
final project/web-game-shop/src/app/models/routing/routing-module.ts

@@ -0,0 +1,22 @@
+import {RouterModule, Routes} from '@angular/router';
+
+import {NgModule} from '@angular/core';
+import {StorefrontComponent} from '../../components/storefront/storefront';
+import {GameCatalogComponent} from '../../components/game-catalog/game-catalog';
+import {GameDetailsComponent} from '../../components/game-details/game-details';
+
+export const routes: Routes = [
+  { path: 'storefront', component: StorefrontComponent },
+  { path: 'catalog', component: GameCatalogComponent },
+
+
+  { path: 'games/:id', component: GameDetailsComponent },
+  { path: '', redirectTo: '/storefront', pathMatch: 'full' },
+
+];
+
+@NgModule({
+  imports: [RouterModule.forRoot(routes, { enableTracing: true })],
+  exports: [RouterModule]
+})
+export class AppRoutingModule { }

+ 16 - 0
final project/web-game-shop/src/app/services/api.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { ApiService } from './api';
+
+describe('Api', () => {
+  let service: ApiService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(ApiService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 38 - 0
final project/web-game-shop/src/app/services/api.ts

@@ -0,0 +1,38 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { User } from '../models/user.model';
+import { Game } from '../models/game.model';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ApiService {
+  private baseUrl = 'http://localhost:8085/api';
+
+  constructor(private http: HttpClient) {}
+
+  // User management endpoints
+  getUsers(): Observable<User[]> {
+    return this.http.get<User[]>(`${this.baseUrl}/users`);
+  }
+
+  registerUser(user: User): Observable<User> {
+    return this.http.post<User>(`${this.baseUrl}/users/register`, user);
+  }
+// Initiates Stripe Checkout compiler sequences by fetching the dynamic mapping redirect URL
+  createStripeSession(userId: number, amount: number): Observable<{ url: string }> {
+    return this.http.post<{ url: string }>('http://localhost:8085/api/payment/create-checkout-session', {
+      userId: userId,
+      amount: amount
+    });
+  }
+
+  // Requests the operational simulation pipeline wrapper to instantly verify balance records
+  simulateWebhookPayment(userId: number, amount: number): Observable<any> {
+    return this.http.post('http://localhost:8085/api/payment/simulate-success-webhook', {
+      userId: userId,
+      amount: amount
+    });
+  }
+}

+ 16 - 0
final project/web-game-shop/src/app/services/bug-report/bug-report.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { BugReportService } from './bug-report';
+
+describe('BugReport', () => {
+  let service: BugReportService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(BugReportService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 16 - 0
final project/web-game-shop/src/app/services/bug-report/bug-report.ts

@@ -0,0 +1,16 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class BugReportService {
+  private baseUrl = 'http://localhost:8085/api/bugs';
+
+  constructor(private http: HttpClient) {}
+
+  submitBugReport(reportData: any): Observable<any> {
+    return this.http.post(this.baseUrl, reportData);
+  }
+}

+ 16 - 0
final project/web-game-shop/src/app/services/cart/cart.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { CartService } from './cart';
+
+describe('Cart', () => {
+  let service: CartService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(CartService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 88 - 0
final project/web-game-shop/src/app/services/cart/cart.ts

@@ -0,0 +1,88 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { BehaviorSubject, Observable, throwError } from 'rxjs';
+import { Game } from '../../models/game.model';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class CartService {
+  private cartItems: Game[] = [];
+  private orderApiUrl = 'http://localhost:8085/api/orders';
+
+  // Reactive stream to broadcast cart item count
+  private cartCountSubject = new BehaviorSubject<number>(0);
+  cartCount$ = this.cartCountSubject.asObservable();
+
+  constructor(private http: HttpClient) {
+    // Load existing cart items from localStorage on initialization
+    const savedCart = localStorage.getItem('nexus_cart');
+    if (savedCart) {
+      this.cartItems = JSON.parse(savedCart);
+    }
+    // Initialize the subject with the loaded cart length
+    this.cartCountSubject.next(this.cartItems.length);
+  }
+
+  /**
+   * Adds a game to the local cart storage.
+   */
+  addToCart(game: Game): void {
+    const isAlreadyInCart = this.cartItems.some(item => item.gameId === game.gameId);
+    if (!isAlreadyInCart) {
+      this.cartItems.push(game);
+      this.saveCartToStorage();
+    }
+  }
+
+  /**
+   * Retrieves all items currently in the cart.
+   */
+  getCartItems(): Game[] {
+    return this.cartItems;
+  }
+
+  /**
+   * Removes an item from the cart by its ID.
+   */
+  removeFromCart(gameId: number | undefined): void {
+    this.cartItems = this.cartItems.filter(item => item.gameId !== gameId);
+    this.saveCartToStorage();
+  }
+
+  /**
+   * Clears all items from the cart.
+   */
+  clearCart(): void {
+    this.cartItems = [];
+    this.saveCartToStorage();
+  }
+
+  /**
+   * Dispatches a purchase request for a specific game to the Spring Boot OrderController.
+   */
+  checkoutGame(userId: number, gameId: number | undefined): Observable<any> {
+    // Ensure gameId is defined before proceeding with the request
+    if (gameId === undefined || gameId === null) {
+      console.error('Service Error: gameId is undefined.');
+      return throwError(() => new Error('Game identifier cannot be undefined.'));
+    }
+
+    const params = new HttpParams()
+      .set('userId', userId.toString())
+      .set('gameId', gameId.toString());
+
+    return this.http.post<any>(`${this.orderApiUrl}/purchase`, {}, { params });
+  }
+
+  private saveCartToStorage(): void {
+    localStorage.setItem('nexus_cart', JSON.stringify(this.cartItems));
+    // Broadcast updated length to all subscribers
+    this.cartCountSubject.next(this.cartItems.length);
+  }
+
+  // Explicit manual update method if needed elsewhere
+  updateCartItemCount(length: number): void {
+    this.cartCountSubject.next(length);
+  }
+}

+ 16 - 0
final project/web-game-shop/src/app/services/developer/developer-service.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { DeveloperService } from './developer-service';
+
+describe('DeveloperService', () => {
+  let service: DeveloperService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(DeveloperService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 6 - 0
final project/web-game-shop/src/app/services/developer/developer-service.ts

@@ -0,0 +1,6 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class DeveloperService {}

+ 16 - 0
final project/web-game-shop/src/app/services/game/game.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { Game } from './game';
+
+describe('Game', () => {
+  let service: Game;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(Game);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 47 - 0
final project/web-game-shop/src/app/services/game/game.ts

@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { Game } from '../../models/game.model';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class GameService {
+  private baseUrl = 'http://localhost:8085/api/games';
+  public hostMediaRoot = 'http://localhost:8085';
+
+  constructor(private http: HttpClient) {}
+
+  getGames(): Observable<any> {
+    return this.http.get<any>(this.baseUrl);
+  }
+
+  createGame(formData: FormData): Observable<Game> {
+    return this.http.post<Game>(this.baseUrl, formData);
+  }
+
+  updateGame(gameId: number, formData: FormData): Observable<Game> {
+    return this.http.put<Game>(`${this.baseUrl}/${gameId}`, formData);
+  }
+
+  deleteGame(gameId: number): Observable<void> {
+    return this.http.delete<void>(`${this.baseUrl}/${gameId}`);
+  }
+
+  getGameById(id: number): Observable<Game> {
+    return this.http.get<Game>(`${this.baseUrl}/${id}`);
+  }
+
+  buyGame(gameId: number, userId: number): Observable<any> {
+    return this.http.post<any>(`${this.baseUrl}/${gameId}/buy?userId=${userId}`, {});
+  }
+
+
+  getReportsByGame(gameId: number): Observable<Report[]> {
+    return this.http.get<Report[]>(`${this.baseUrl}/${gameId}/reports`);
+  }
+
+  updateReportStatus(gameId: number, reportId: number, status: string): Observable<Report> {
+    return this.http.put<Report>(`${this.baseUrl}/${gameId}/reports/${reportId}/status?status=${status}`, {});
+  }
+}

+ 16 - 0
final project/web-game-shop/src/app/services/library/library.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { LibraryService } from './library';
+
+describe('Library', () => {
+  let service: LibraryService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(LibraryService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 39 - 0
final project/web-game-shop/src/app/services/library/library.ts

@@ -0,0 +1,39 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Observable, throwError } from 'rxjs';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class LibraryService {
+  private baseUrl = 'http://localhost:8085/api/library';
+
+  constructor(private http: HttpClient) {}
+
+  purchaseAndAddGame(userId: number, gameId: number | undefined): Observable<any> {
+    if (gameId === undefined || gameId === null) {
+      console.error('Service rejection: Invalid gameId parameter provided.');
+      return throwError(() => new Error('Missing game identifier reference.'));
+    }
+
+    const params = new HttpParams()
+      .set('userId', userId.toString())
+      .set('gameId', gameId.toString());
+
+    return this.http.post<any>(`${this.baseUrl}/add`, {}, { params });
+  }
+
+  /**
+   * Fetches list of library items (games) for the specific user.
+   */
+  getUserLibrary(userId: number): Observable<any[]> {
+    return this.http.get<any[]>(`${this.baseUrl}/user/${userId}/items`);
+  }
+
+  refundGame(userId: number, itemId: number): Observable<any> {
+    const params = new HttpParams()
+      .set('userId', userId.toString())
+      .set('itemId', itemId.toString());
+    return this.http.delete(`${this.baseUrl}/refund`, { params, responseType: 'text' });
+  }
+}

+ 16 - 0
final project/web-game-shop/src/app/services/user/user.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { User } from './user';
+
+describe('User', () => {
+  let service: User;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(User);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 74 - 0
final project/web-game-shop/src/app/services/user/user.ts

@@ -0,0 +1,74 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, BehaviorSubject } from 'rxjs';
+import { User } from '../../models/user.model';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class UserService {
+  private baseUrl = 'http://localhost:8085/api/users';
+
+  // Reactive session stream holding the active parsed user object or null if guest
+  private currentSessionSubject = new BehaviorSubject<any>(null);
+  public currentSession$ = this.currentSessionSubject.asObservable();
+
+  constructor(private http: HttpClient) {
+    // Automatically initialize stream status from storage on application startup boot phase
+    this.loadInitialSession();
+  }
+
+  private loadInitialSession(): void {
+    const activeSessionStr = localStorage.getItem('nexus_mock_session');
+    if (activeSessionStr) {
+      try {
+        this.currentSessionSubject.next(JSON.parse(activeSessionStr));
+      } catch (e) {
+        console.error('Initial session recovery failed:', e);
+      }
+    }
+  }
+
+  // Notifies all reactive stream observers across the system that a new identity is confirmed
+  updateSessionState(sessionData: any): void {
+    localStorage.setItem('nexus_mock_session', JSON.stringify(sessionData));
+    this.currentSessionSubject.next(sessionData);
+  }
+
+  // Clear tracking tokens and broadcast the null context downstream to trigger logout transitions
+  clearSessionState(): void {
+    localStorage.removeItem('nexus_mock_session');
+    this.currentSessionSubject.next(null);
+  }
+
+  register(userData: User): Observable<User> {
+    return this.http.post<User>(`${this.baseUrl}/register`, userData);
+  }
+
+  login(credentials: { email: any; password: any }): Observable<User> {
+    return this.http.post<User>(`${this.baseUrl}/login`, {
+      email: credentials.email,
+      password: credentials.password
+    });
+  }
+
+  getUserById(userId: number): Observable<User> {
+    return this.http.get<User>(`${this.baseUrl}/${userId}`);
+  }
+
+// Initiates Stripe Checkout compiler sequences by fetching the dynamic mapping redirect URL
+  createStripeSession(userId: number, amount: number): Observable<{ url: string }> {
+    return this.http.post<{ url: string }>('http://localhost:8085/api/payment/create-checkout-session', {
+      userId: userId,
+      amount: amount
+    });
+  }
+
+  // Requests the operational simulation pipeline wrapper to instantly verify balance records
+  simulateWebhookPayment(userId: number, amount: number): Observable<any> {
+    return this.http.post('http://localhost:8085/api/payment/simulate-success-webhook', {
+      userId: userId,
+      amount: amount
+    });
+  }
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов